나만의 React 유효성 검사 라이브러리 만들기: 기능(2부)

게시 됨: 2022-03-10
빠른 요약 ↬ Kristofer의 이전 기사에서 그는 유효성 검사 라이브러리의 기본 부분을 구현하는 방법을 설명했습니다. 다음 부분에서는 개발자 경험을 개선하는 데 중점을 둘 것이지만 오늘의 기사에서는 Part 1에서 만든 기능에 더 많은 기능을 추가하는 데 중점을 둘 것입니다.

유효성 검사 라이브러리를 구현하는 것은 그리 어렵지 않습니다. 유효성 검사 라이브러리를 다른 보다 훨씬 더 좋게 만드는 추가 기능을 모두 추가하는 것도 아닙니다.

이 기사에서는 이 기사 시리즈의 이전 부분에서 구현하기 시작한 유효성 검사 라이브러리를 계속 구현합니다. 이것들은 단순한 개념 증명에서 실제 사용 가능한 라이브러리로 우리를 데려갈 기능입니다!

  • 1부: 기본 사항
  • 2부: 기능
  • 3부: 경험

제출 시에만 확인 표시

모든 변경 이벤트에 대해 유효성을 검사하기 때문에 좋은 사용자 경험을 위해 너무 일찍 사용자 오류 메시지를 표시하고 있습니다. 이를 완화할 수 있는 몇 가지 방법이 있습니다.

첫 번째 솔루션은 submitted 플래그를 useValidation 후크의 반환된 속성으로 제공하는 것입니다. 이렇게 하면 오류 메시지를 표시하기 전에 양식이 제출되었는지 여부를 확인할 수 있습니다. 여기서 단점은 "오류 코드 표시"가 조금 더 길어진다는 것입니다.

 <label> Username <br /> <input {...getFieldProps('username')} /> {submitted && errors.username && ( <div className="error">{errors.username}</div> )} </label>

또 다른 접근 방식은 두 번째 오류 집합을 제공하는 것입니다(submitErrors라고 함). submittedErrors submitted 오류가 false이면 빈 개체이고 true이면 errors 개체입니다. 다음과 같이 구현할 수 있습니다.

 const useValidation = config => { // as before return { errors: state.errors, submittedErrors: state.submitted ? state.errors : {}, }; }

이런 식으로 우리는 보여주고 싶은 오류 유형을 간단히 구조화할 수 있습니다. 물론 호출 사이트에서도 이 작업을 수행할 수 있지만 여기에서 제공함으로써 모든 소비자 내부에서가 아니라 한 번만 구현합니다.

  • submittedErrors 사용 방법을 보여주는 CodeSandbox 데모를 참조하십시오.
점프 후 더! 아래에서 계속 읽기 ↓

블러 시 오류 메시지 표시

많은 사람들이 특정 필드를 벗어나면 오류가 표시되기를 원합니다. 어떤 필드가 "흐리게 처리되었는지"(다른 곳으로 이동) 추적하고 위의 submittedErrors 와 유사한 객체 blurredErrors 를 반환하여 이에 대한 지원을 추가할 수 있습니다.

구현을 위해서는 새로운 작업 유형인 blur 를 처리해야 하며, 이는 blurred 라는 새로운 상태 객체를 업데이트할 것입니다.

 const initialState = { values: {}, errors: {}, blurred: {}, submitted: false, }; function validationReducer(state, action) { switch (action.type) { // as before case 'blur': const blurred = { ...state.blurred, [action.payload]: true }; return { ...state, blurred }; default: throw new Error('Unknown action type'); } }

blur 작업을 전달할 때 필드 이름을 키로 사용하여 blurred 상태 객체에 새 속성을 생성하여 해당 필드가 블러되었음을 나타냅니다.

다음 단계는 적용 가능한 경우 이 작업을 전달하는 onBlur 소품을 getFieldProps 함수에 추가하는 것입니다.

 getFieldProps: fieldName => ({ // as before onBlur: () => { dispatch({ type: 'blur', payload: fieldName }); }, }),

마지막으로 필요할 때만 오류를 표시할 수 있도록 useValidation 후크에서 blurredErrors 를 제공해야 합니다.

 const blurredErrors = useMemo(() => { const returnValue = {}; for (let fieldName in state.errors) { returnValue[fieldName] = state.blurred[fieldName] ? state.errors[fieldName] : null; } return returnValue; }, [state.errors, state.blurred]); return { // as before blurredErrors, };

여기에서 필드가 흐려졌는지 여부에 따라 표시할 오류를 파악하는 메모이제된 함수를 만듭니다. 오류 또는 흐릿한 개체가 변경될 때마다 이 오류 집합을 다시 계산합니다. 문서에서 useMemo 후크에 대해 자세히 알아볼 수 있습니다.

  • CodeSandbox 데모 보기

작은 리팩터를 위한 시간

useValidation 구성 요소는 이제 세 가지 오류 세트를 반환합니다. 대부분은 특정 시점에서 동일하게 보일 것입니다. 이 경로를 따라가는 대신 사용자가 양식의 오류가 표시되기를 원할 때 구성에서 지정하도록 할 것입니다.

새로운 옵션인 showErrors 는 "제출"(기본값), "항상" 또는 "흐림"을 허용합니다. 필요한 경우 나중에 더 많은 옵션을 추가할 수 있습니다.

 function getErrors(state, config) { if (config.showErrors === 'always') { return state.errors; } if (config.showErrors === 'blur') { return Object.entries(state.blurred) .filter(([, blurred]) => blurred) .reduce((acc, [name]) => ({ ...acc, [name]: state.errors[name] }), {}); } return state.submitted ? state.errors : {}; } const useValidation = config => { // as before const errors = useMemo( () => getErrors(state, config), [state, config] ); return { errors, // as before }; };

오류 처리 코드가 우리 공간의 대부분을 차지하기 시작했기 때문에 우리는 그것을 자체 기능으로 리팩토링하고 있습니다. Object.entries.reduce 항목을 따르지 않는 경우 - 괜찮습니다. 마지막 섹션의 for...in 코드를 다시 작성하는 것입니다.

onBlur 또는 즉각적인 유효성 검사가 필요한 경우 useValidation 구성 개체에 showError prop을 지정할 수 있습니다.

 const config = { // as before showErrors: 'blur', }; const { getFormProps, getFieldProps, errors } = useValidation(config); // errors would now only include the ones that have been blurred
  • CodeSandbox 데모 보기

가정에 대한 참고 사항

“저는 이제 각 양식이 동일한 방식으로 오류를 표시하기를 원할 것이라고 가정하고 있습니다(항상 제출, 항상 흐림 등). 그것은 대부분의 응용 프로그램에 해당할 수 있지만 아마도 모든 응용 프로그램에 해당되는 것은 아닙니다. 가정을 인식하는 것은 API를 만드는 데 있어 부분을 차지합니다.”

교차 검증 허용

유효성 검사 라이브러리의 정말 강력한 기능은 교차 유효성 검사를 허용하는 것입니다. 즉, 한 필드의 유효성 검사를 다른 필드 값에 기반합니다.

이를 허용하려면 사용자 정의 후크가 객체 대신 함수를 허용하도록 해야 합니다. 이 함수는 현재 필드 값으로 호출됩니다. 그것을 구현하는 것은 실제로 단 세 줄의 코드입니다!

 function useValidation(config) { const [state, dispatch] = useReducer(...); if (typeof config === 'function') { config = config(state.values); } }

이 기능을 사용하려면 구성 객체를 useValidation 에 반환하는 함수를 전달하면 됩니다.

 const { getFieldProps } = useValidation(fields => ({ password: { isRequired: { message: 'Please fill out the password' }, }, repeatPassword: { isRequired: { message: 'Please fill out the password one more time' }, isEqual: { value: fields.password, message: 'Your passwords don\'t match' } } }));

여기에서는 두 개의 비밀번호 필드에 동일한 입력이 포함되도록 fields.password 값을 사용합니다(이는 끔찍한 사용자 경험이지만 다른 블로그 게시물을 위한 것입니다).

  • 사용자 이름과 암호가 같은 값이 되지 않도록 하는 CodeSandbox 데모를 참조하세요.

접근성 향상 추가

필드의 소품을 담당할 때 해야 할 일은 기본적으로 올바른 aria-tag를 추가하는 것입니다. 이것은 스크린 리더가 양식을 설명하는 데 도움이 됩니다.

매우 간단한 개선 사항은 필드에 오류가 있는 경우 aria-invalid="true" 를 추가하는 것입니다. 다음을 구현해 보겠습니다.

 const useValidation = config => { // as before return { // as before getFieldProps: fieldName => ({ // as before 'aria-invalid': String(!!errors[fieldName]), }), } };

이것은 줄의 추가된 코드이며 스크린 리더 사용자를 위한 훨씬 더 나은 사용자 경험입니다.

String(!!state.errors[fieldName]) 을 작성하는지 궁금할 것입니다. state.errors[fieldName] 은 문자열이고 이중 부정 연산자는 부울 값을 제공합니다(단순히 참 또는 거짓 값이 아님). 그러나 aria-invalid 속성은 문자열이어야 합니다("true" 또는 "false" 외에 "grammar" 또는 "spelling"도 읽을 수 있음). 따라서 해당 부울을 해당 문자열로 강제 변환해야 합니다.

접근성을 개선하기 위해 할 수 있는 몇 가지 조정이 더 있지만 이것은 공정한 시작처럼 보입니다.

약식 유효성 검사 메시지 구문

calidators 패키지에 있는 대부분의 유효성 검사기(및 대부분의 다른 유효성 검사기)에는 오류 메시지만 필요합니다. 해당 문자열을 포함하는 message 속성이 있는 객체 대신 해당 문자열을 전달할 수 있다면 좋지 않을까요?

validateField 함수에서 이를 구현해 보겠습니다.

 function validateField(fieldValue = '', fieldConfig, allFieldValues) { for (let validatorName in fieldConfig) { let validatorConfig = fieldConfig[validatorName]; if (typeof validatorConfig === 'string') { validatorConfig = { message: validatorConfig }; } const configuredValidator = validators[validatorName](validatorConfig); const errorMessage = configuredValidator(fieldValue); if (errorMessage) { return errorMessage; } } return null; }

이렇게 하면 유효성 검사 구성을 다음과 같이 다시 작성할 수 있습니다.

 const config = { username: { isRequired: 'The username is required', isEmail: 'The username should be a valid email address', }, };

훨씬 깨끗합니다!

초기 필드 값

때로는 이미 작성된 양식의 유효성을 검사해야 합니다. 우리의 커스텀 훅은 아직 지원하지 않습니다 — 이제 시작하겠습니다!

초기 필드 값은 속성 initialValue 의 각 필드에 대한 구성에서 지정됩니다. 지정하지 않으면 기본적으로 빈 문자열이 사용됩니다.

우리는 감속기의 초기 상태를 생성할 getInitialState 함수를 생성할 것입니다.

 function getInitialState(config) { if (typeof config === 'function') { config = config({}); } const initialValues = {}; const initialBlurred = {}; for (let fieldName in config.fields) { initialValues[fieldName] = config.fields[fieldName].initialValue || ''; initialBlurred[fieldName] = false; } const initialErrors = validateFields(initialValues, config.fields); return { values: initialValues, errors: initialErrors, blurred: initialBlurred, submitted: false, }; }

모든 필드를 살펴보고 initialValue 속성이 있는지 확인하고 그에 따라 초기 값을 설정합니다. 그런 다음 유효성 검사기를 통해 초기 값을 실행하고 초기 오류도 계산합니다. 초기 상태 객체를 반환한 다음 useReducer 후크에 전달할 수 있습니다.

필드 구성에 non-validator prop을 도입하기 때문에 필드를 검증할 때 이를 건너뛸 필요가 있습니다. 이를 위해 validateField 함수를 변경합니다.

 function validateField(fieldValue = '', fieldConfig) { const specialProps = ['initialValue']; for (let validatorName in fieldConfig) { if (specialProps.includes(validatorName)) { continue; } // as before } }

이와 같은 기능을 계속 추가하면서 specialProps 배열에 추가할 수 있습니다.

  • CodeSandbox 데모 보기

합산

우리는 놀라운 유효성 검사 라이브러리를 만들기 위해 순조롭게 진행 중입니다. 우리는 수많은 기능을 추가했으며 지금쯤이면 우리는 많은 생각을 하는 리더가 되었습니다.

이 시리즈의 다음 부분에서는 LinkedIn에서 유효성 검사 라이브러리를 트렌드로 만드는 모든 추가 기능을 추가할 것입니다.