Создание собственной библиотеки валидации для React: опыт разработки (часть 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 и получим доступ к свойствам валидации (getFormPropserrors и т. д.) с помощью хука 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, обеспечивает гибкость и множество полезных для разработчика функций.

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

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

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

Данная публикация представляет собой перевод статьи «Creating Your Own React Validation Library: The Developer Experience (Part 3)» , подготовленной дружной командой проекта Интернет-технологии.ру