Создание собственной библиотеки валидации на React: дополнительные функции (часть 2)

В данной статье будет продолжена реализация библиотеки валидации, которую мы начали создавать в предыдущей части. Сейчас мы реализуем дополнительный функционал.

Показ валидации только при отправке данных

Мы проверяем все события change. Так что сообщения об ошибках отображаются посетителю слишком рано. Есть пару методов исправить это.

Первое решение — использовать флаг submitted в виде возвращаемого свойства хука useValidation. Так мы сможем проверить, отправлены ​​ли данные, перед выводом сообщения об ошибке. Недостатком подобного подхода будет запоздалое вывод кода ошибки:

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

Другой подход заключается в использовании второго набора ошибок( submittedErrors). Он пустой, если submitted равен false, и включает объект errors, если submitted равен true. Мы можем реализовать это следующим образом:

const useValidation = config => { // как и раньше return { errors: state.errors, submittedErrors: state.submitted? state.errors: {}, };}

Посмотрите демо-версию, демонстрирующую использование submittedErrors.

Вывод сообщения об ошибках при выходе из поля ввода

Многие посетители хотят, чтобы сообщение об ошибке выводилось, когда они покидают поле ввода формы. Это можно реализовать, отслеживая, какие поля ввода утратили фокус ввода. А также возвращая объект blurredErrors, аналогичный описанному выше submittedErrors.

Реализация потребует от нас обработки нового события – blur. Оно будет обновлять новый объект состояния с именем blurred:

const initialState = { values: {}, errors: {}, blurred: {}, submitted: false,};function validationReducer(state, action) { switch(action.type) { // как и раньше 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 =>({  // как и раньше onBlur:() => {    dispatch({ type: 'blur', payload: fieldName });  },}),

Также необходимо задействовать blurredErrors из хука useValidation , чтобы выводить ошибки только при необходимости.

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 {  // как и раньше blurredErrors,};

Здесь мы создаем мемоизированную возможность , которая определяет, какие ошибки выводить на основе данных о том, было ли поле ввода покинуто посетителем или нет. Мы пересчитываем этот набор ошибок всякий раз, когда изменяются ошибки или объекты blurred. Вы можете без труда найти больше информации о хуке useMemo в документации .

Посмотреть просмотр

Выполняем незначительный рефакторинг

Компонент useValidation сейчас возвращает три набора ошибок, большинство из которых в определенный момент времени будут выглядеть одинаково. Но мы позволим посетителям указывать в конфигурации, когда они хотят видеть ошибки.

Новый настройка showErrors принимает значения “submit” (по умолчанию), “always” или “blur”.

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 => {  // как и раньше const errors = useMemo(    () => getErrors(state, config),     [state, config]  );  return {    errors,    // как и раньше  };};

Код обработки ошибок начал занимать большую часть библиотеки, так что поместим его в отдельную возможность.

Если нам требуется onBlur или мгновенная проверка, необходимо указать свойство showError в объекте конфигурации useValidation .

const config = {  // как и раньше showErrors: 'blur',};const { getFormProps, getFieldProps, errors } = useValidation(config);// ошибки сейчас будут выводиться, когда поле ввода было покинуто

Посмотреть просмотр

Разрешаем перекрестную валидацию

Реализуем перекрестную валидацию. То есть, валидацию одного поля ввода, основываясь на значении иного. Для этого надо заставить наш пользовательский хук принимать возможность вместо объекта. Эта функцию будет вызываться с текущими значениями поля ввода:

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 , чтобы убедиться, что два поля ввода пароля содержат одинаковое значение.

Посмотрите просмотр , в котором реализована проверка повторного ввода пароля.

Элементы доступности

Также следует добавить правильные теги aria по умолчанию. Это поможет посетителям с ограниченными функциями правильно понять форму.

Для этого можно использовать aria-invalid="true" , если в поле ввода есть ошибка. Давайте реализуем это:

const useValidation = config => {  // как и раньше return {    // как и раньше getFieldProps: fieldName => ({      // как и раньше      'aria-invalid': String(!!errors[fieldName]),    }),  }};

Сокращенный синтаксис сообщений валидации

Для большинства валидаторов, присутствующих в пакете 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 .

Поскольку мы вводим в конфигурацию полей ввода свойство без валидатора. Так что необходимо пропустить его, когда мы проверяем поля ввода. Для этого изменим возможность validateField :

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

Добавить иные дополнительные возможности можно, используя массив specialProps .

Посмотреть просмотр

Заключение

В следующей части серии статей мы добавим функционал, который сделает нашу библиотеку валидации трендовой даже в LinkedIn.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *