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

Заключительная часть серии статей будет посвящена улучшению пользовательского интерфейса библиотеки. Теперь единственный недостаток решения заключается в том, что с библиотекой не удобно работать. Но мы это исправим.

  • Часть 1: Основные возможности
  • Часть 2: Дополнительные возможности
  • Часть 3: Опыт

С чего начать

В предыдущей части данной серии статей мы вынесли весь программный код библиотеки в отдельные файлы. Посмотрите демо-версию проекта.

Удобство использования

Библиотека должна быть максимально простой в использовании. Для этого необходимо добавить в нее удобные служебные возможности.

Одной из таких возможностей может быть проверка корректности работы формы. То есть, все ли сообщения об ошибках равны null. Давайте реализуем это:

const isFormValid = useMemo(() => Object.values(errors).every(error => error === null), [errors]);

Мы предоставляем этот флаг в обработчике форм onSubmit, а также в способе рендеринга.

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

Отображение предупреждений

Мы создадим две возможности: warning — для регистрации предупреждений в консоли и invariant — для вывода сообщений ошибок.

function warning(condition, message) { if(process.env.NODE_ENV === 'production' || condition) { return; } console.warn('useValidation: ' + message);}function invariant(condition, message) { if(process.env.NODE_ENV === 'production' || condition) { return; } throw new Error('useValidation: ' + message);}

Необходимо использовать возможность invariant, если ошибка приведет к сбою библиотеки. А также возможность warning для вывода предупреждений для разработчика.

Когда отображать предупреждения

Библиотека принимает довольно большой объект конфигурации, так что необходимо проверить его. Для этого мы могли бы использовать TypeScript или Flow. Но это исключит всех посетителей обычного JavaScript.

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

function validateConfigSchema(config) { if(process.env.NODE_ENV === 'production') { return; } if(typeof config === 'function') { config = config({}); } invariant(  typeof config === 'object',    `useValidation should be called with an object or a function returning an object. You passed a ${typeof config}.`,);  invariant(    typeof config.fields === 'object',    'useValidation requires a `field` prop with an object containing the fields and their validators. Please refer to the documentation on usage: https://link.to/docs');    invariant(    Object.values(config.fields).every(field => typeof field === 'object'),    'useValidation requires that the `field` object only contains objects. It looks like yours isn't. Please refer to the documentation on usage: https://link.to/docs');  warning(    ['always', 'blur', 'submit', undefined].includes(config.showError),    'useValidation received an unsupported value in the `showError` prop. Valid values are "always", "blur" or "submit".')  // И так далее}

Но можно и не писать этот код вручную. Есть специальное средство для проверки схемы объекта под названием joi. Оно может помочь с созданием надежной проверки во время выполнения.

Большая гибкость

Хороший опыт разработчиков в значительной степени определяется гибкостью. Рассмотрим пару методов улучшить этот опыт.

Сопоставление конфликтующих свойств

Используемые нами геттеры применяют некоторые свойства к полям ввода ввода и форме, которые могут быть случайно изменены. Так что добавим к геттерам объект переопределения свойств, который будет сопоставлять любые конфликтующие свойства.

Пример реализации в getFieldProps:

getFieldProps:(fieldName, overrides = {}) =>({  onChange: e => {    const { value } = e.target;    if(!config.fields[fieldName]) {      return;    }    dispatch({      type: 'change',      payload: { [fieldName]: value },    });    if(overrides.onChange) {      overrides.onChange(e);    }  },  onBlur: e => {    dispatch({ type: 'blur', payload: fieldName });    if(overrides.onBlur) {      overrides.onBlur(e)    }  },  name: overrides.name || fieldName,  value: state.values[fieldName] || '',}),

Подобный подход может быть использован и в getFormProps.

Предотвращение сквозной передачи свойств

Некоторые формы могут быть большими и разделенными на пару компонентов. Вместо того чтобы выполнять передачу свойств вниз по дереву мы используем контекст.

Сначала создадим контекст ValidationContext при помощи способа createContext:

export const ValidationContext = React.createContext({});

Далее компонент ValidationProvider. Он передает в контекст все значения из хука useValidation:

export const ValidationProvider = props => {  const context = useValidation(props.config);  return(          {props.children}    );};

Сейчас вместо вызова useValidation обернем форму в компонент ValidationProvider и получим доступ к свойствам валидации ( getFormProps , errors и т. д.) при помощи хука useContext . Это будет выглядеть следующим образом:

Import React, { useContext } from 'react';import { ValidationContext } from './useValidation';function UsernameForm(props) {  const { getFieldProps, errors } = useContext(ValidationContext);  return (    <>      <input {...getFieldProps('username')} />      {errors.username && {errors.username}></span>}    </>  );}

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

Документация — это ключ ко всему

Первым этапом в документировании библиотеки должно стать составление простого для понимания файла README. Он будет содержать основные примеры использования библиотеки.

После этого можно легко запустить веб-сайт со справочной документацией. На нем вы сможете без труда разместить подробную документацию по API, советы для стандартных случаев использования и FAQ.

Существуют отличные инструменты для создания справочных ресурсов. Мой фаворит – docusaurus Facebook .

Заключение

В данной серии статей мы создали хорошую библиотеку валидации. Она предлагает простой API, обеспечивает гибкость и несколько полезных для разработчика возможностей.

Окончательный вариант исходного кода:

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

Спасибо за прочтение!

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

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