Felte: Расширяемая библиотека форм для React

ПРИМЕЧАНИЕ: Предыдущие статьи цикла были обновлены для версии 1!

Пожалуй, одна из самых распространенных проблем, которую приходится решать front-end разработчикам, — это работа с формами. Особенно в современных веб-приложениях, которые требуют мгновенной валидации и других взаимодействий с пользователем в реальном времени. Чтобы обеспечить как можно лучший пользовательский опыт, вы, вероятно, воспользуетесь сторонней библиотекой управления формами.

В этом посте я собираюсь написать о Felte, библиотеке управления формами для React, над которой я работал в течение последнего года и которая призвана максимально упростить основы работы с формами на фронт-энде, но при этом позволяет усложнять их по мере роста ваших требований.

Это одна из трех записей блога, связанных с Felte. Этот пост ориентирован на интеграцию Felte с React. Два других посвящены интеграции Felte с Svelte и Solid.

Особенности

Как упоминалось выше, Felte стремится сделать основы реактивности формы как можно более простыми в обращении, но при этом позволяет реализовать более сложное поведение с помощью конфигурации и расширяемости. Его основными особенностями являются:

  • Одно действие, чтобы сделать вашу форму реактивной.
  • Использование родных элементов HTML5 для создания формы. (Необходим только атрибут name).
  • Минимальные повторные рендеринги. Никаких, если вам не нужны данные формы в вашем компоненте.
  • Предоставляет хранилища и вспомогательные функции для обработки более сложных случаев использования.
  • Никаких предположений относительно вашей стратегии валидации. Используйте любую библиотеку валидации или напишите свою собственную стратегию.
  • Обработка добавления и удаления элементов управления формы во время выполнения.
  • Официальные решения для отчетности об ошибках с помощью пакетов reporter.
  • Поддерживает валидацию с помощью yup, zod, superstruct и vest.
  • Легко расширяет свою функциональность.

Как это выглядит?

В своей самой базовой форме Felte требует импорта только одной функции:

import { useForm } from '@felte/react';

export function Form() {
  const { form } = useForm({
    onSubmit: (values) => {
      // ...
    },
  });

  return (
    <form ref={form}>
      <input type="text" name="email" />
      <input type="password" name="password" />
      <input type="submit" value="Sign in" />
    </form>
  );
}
Войти в полноэкранный режим Выход из полноэкранного режима

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

{
  email: '',
  password: '',
}
Войти в полноэкранный режим Выход из полноэкранного режима

Где я могу увидеть свои данные?

По мере ввода текста Felte будет отслеживать ввод пользователя в наблюдаемой таблице, которая содержит данные вашей формы в той же форме, что и значения, которые вы получили бы на onSubmit. Эта наблюдаемая обрабатывается Felte, и ее значение можно получить, вызвав функцию data, возвращаемую из useForm; нет необходимости обрабатывать наблюдаемые самостоятельно! В дальнейшем мы будем называть эти функции accessors. Когда этот аксессор вызывается без аргументов (data()), он возвращает все данные формы в виде объекта. Это также «подписывает» ваш компонент на каждое изменение в форме, вызывая повторный рендеринг при каждом изменении значения. Для выбора конкретного поля в качестве первого аргумента может быть передан аргумент, либо селекторная функция, либо строковый путь. Используя аргумент, ваш компонент будет «подписываться» только на изменения, сделанные для определенного значения, которое вы выбрали.

Например, это позволит записывать в консоль электронную почту пользователя по мере ее ввода:

// Within a component
const { form, data } = useForm({ /* ... */ });

// Passing a function as first argument
console.log(data(($data) => $data.email));

// Passing a string as first argument
console.log(data('email'));
Войти в полноэкранный режим Выйти из полноэкранного режима

Аксессоры НЕ являются крючками, это означает, что их можно вызывать из любого места в коде.

Здесь может понадобиться валидация

Конечно, еще одним общим требованием к формам является валидация. Если мы хотим, чтобы наше приложение было удобным для пользователя, нам понадобится валидация на стороне клиента. Объект конфигурации useForm принимает функцию validate (которая может быть асинхронной). Она будет получать текущее значение ваших data по мере его изменения, и ожидает, что вы вернете объект с той же формой, содержащий ваши сообщения о валидации, если форма не валидна, или ничего, если форма валидна. Felte будет отслеживать эти сообщения валидации в аксессоре, который возвращается из useForm как errors:

const { form, errors } = useForm({
  validate(values) {
    const currentErrors = {};
    if (!values.email) currentErrors.email = 'Must not be empty';
    if (!values.password) currentErrors.password = 'Must not be empty';
    return currentErrors;
  },
});

console.log(errors(($errors) => $errors.email));
Войти в полноэкранный режим Выход из полноэкранного режима

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

Обработка сложных сценариев с помощью расширяемости

Felte не пытается найти идеальное решение для обработки всех сценариев, связанных с управлением формами. Именно поэтому Felte предлагает API для расширения функциональности по мере усложнения ваших требований. Возможно, у вас есть предпочитаемая библиотека, которую вы предпочитаете использовать, например, очень популярная yup или Vest (о которой недавно говорили на Svelte Summit). Изменить поведение Felte для обработки этих сценариев можно с помощью опции extend в объекте конфигурации useForm. Подробнее об этом можно прочитать в официальной документации. Чтобы упростить ситуацию в рамках этой статьи, я расскажу только о некоторых из существующих пакетов, которые мы поддерживаем для обработки некоторых распространенных сценариев использования:

Валидаторы: Интеграция с популярными библиотеками валидации

В настоящее время мы поддерживаем четыре пакета для интеграции Felte с некоторыми популярными библиотеками валидации: yup, zod, superstruct и совсем недавно vest. Здесь мы будем использовать yup в качестве примера, но вы можете прочитать больше об остальных здесь.

Пакет для использования yup находится на npm под именем @felte/validator-yup. Вам нужно будет установить его вместе с yup:

npm install --save @felte/validator-yup yup

# Or, if you use yarn

yarn add @felte/validator-yup yup
Вход в полноэкранный режим Выйти из полноэкранного режима

Этот пакет валидатора экспортирует функцию validator, которую вы можете вызвать с вашей схемой валидации и передать ее результат в опцию extend опции useForm:

import { validator } from '@felte/validator-yup';
import * as yup from 'yup';

const schema = yup.object({
  email: yup.string().email().required(),
  password: yup.string().required(),
});

const { form } = useForm({
  // ...
  extend: validator({ schema }), // OR `extend: [validator({ schema })],`
  // ...
});
Вход в полноэкранный режим Выход из полноэкранного режима

Репортеры: Отображение сообщений о проверке

Отображение сообщений о проверке можно сделать, непосредственно используя аксессор errors, возвращаемый useForm. Сообщения не будут доступны на этом аксессоре, пока не будет выполнено взаимодействие с соответствующим полем.

import { useForm } from '@felte/react';

function Form() {
  const { form, errors } = useForm({ /* ... */ });

  return (
    <form ref={form}>
      <label htmlFor="email">Email:</label>
      <input name="email" type="email" id="email" />
      {!!errors('email') && (
        <span>{errors('email')}</span>
      )}
      <button>Submit</button>
    </form>
  );
}
Вход в полноэкранный режим Выход из полноэкранного режима

Если определенное поле содержит ошибку, Felte присваивает атрибут aria-invalid=true соответствующему вводу.

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

  • Использование компонента React, что дает наибольшую гибкость и позволяет получить доступ к сообщениям проверки глубоко в дереве компонента без необходимости передавать аксессор errors.
  • Модификация DOM напрямую путем добавления и удаления элементов DOM.
  • Использование Tippy.js для отображения сообщений во всплывающей подсказке.
  • Использование встроенного в браузер API проверки ограничений, который может быть менее дружественным для мобильных пользователей.

Для краткости я рассмотрю только первый пакет. Но вы можете прочитать больше об остальных в документации.

Использовать компонент React для получения сообщений валидации можно с помощью пакета @felte/reporter-react. Вам нужно будет добавить его в свой проект с помощью вашего любимого менеджера пакетов:

# npm
npm i -S @felte/reporter-react

# yarn
yarn add @felte/reporter-react
Вход в полноэкранный режим Выйти из полноэкранного режима

Затем вам нужно импортировать функцию reporter для добавления в свойство extend, а также компонент ValidationMessage, который вы будете использовать для получения сообщений валидации:

import { reporter, ValidationMessage } from '@felte/reporter-react';
import { useForm } from '@felte/react';

function Form() {
  const { form } = useForm({
      // ...
      extend: reporter, // or [reporter]
      // ...
    },
  })

 // We assume a single string will be passed as a validation message
 // This can be an array of strings depending on your validation strategy
  return (
    <form ref={form}>
      <input id="email" type="text" name="email" />
      <ValidationMessage for="email">
        {(message) => <span>{message}</span>}
      </ValidationMessage>
      <input type="password" name="password" />
      <ValidationMessage for="password">
        {(message) => <span>{message}</span>}
      </ValidationMessage>
      <input type="submit" value="Sign in" />
    </form>
  );
}
Вход в полноэкранный режим Выход из полноэкранного режима

Следующие шаги

Вы можете узнать больше о Felte на его официальном сайте с некоторыми функциональными примерами. Также есть более сложный пример, демонстрирующий его использование с Tippy.js и Yup, доступный на CodeSandbox.

Заключительные мысли

Я надеюсь, что это было хорошее введение в Felte, и что оно достаточно интересно для вас, чтобы попробовать. Felte уже находится в стабильном состоянии и используется некоторыми людьми. Я также открыт для помощи и предложений, так что не стесняйтесь открыть проблему или сделать запрос на исправление на GitHub.

Оставьте комментарий

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