Огляд бібліотек для форм в React

10 хв. читання

Форми завжди були складним елементом інтерфейсу користувача. Їх потрібно валідувати, відправляти, ініціалізувати з початковими даними. Повертаючись до реалій React проектів, де панують компоненти, рендеринг, стани та Redux, і ще багато різної асинхронщини, у вас вже немає простої DOM-форми з полями, на onSubmit якої ви отримуєте об'єкт з даними. Під вашим керівництвом опиняється цілий батальйон компонентів, які можуть ререндериться на кожен ваш клік, і все це бажано реалізувати в декларативному підході. Дотримуючись канонів компонентної архітектури, ви маєте компонент форми, що зберігає стан форми (значення всіх полів, стан відправлення тощо) та складається з компонентів-полів (варто згадати поняття «Контрольовані компоненти»), що можуть мати асинхронну валідацію, виведення помилок, внутрішню логіку, і до того ж можуть перевикористовуватись.

Для спрощення роботи з формами, на сьогодні, виділив би 2 популярні рішення: redux-form та formik. Ознайомимося з ними детальніше. Поглянемо на те, як вони зберігають стан форм, як відбувається підключення валідації та обробка помилок, та як обробляється відправлення форми.

Redux-form

На мою думку, це самий перевірений (після варіанту власноруч написаної логіки) варіант по роботі з формами в React/Redux стеку. Він має низький поріг входу для новачків. Бібліотека була створена ще у 2015, але сучасний вигляд та ідеологію отримала з виходом 6 версії (на момент написання статті, актуальна версія 7.3)

Як може бути зрозуміло з самої назви, бібліотека базується на взаємодії зі store Redux, в який і зберігається стан активної форми, але з іншого боку це і є найбільшим мінусом — жорстка прив'язка до наявності Redux на проекті. Тому якщо ви вирішили використовувати інший підхід до роботи зі станом застосунку, вам з цією бібліотекою не по дорозі.

Низький поріг входу, на мій погляд, досягається тим, що вам не потрібно будь-яким чином змінювати підхід до опису DOM-структури вашої форми: все той же тег <form> і обробник onSubmit, от тільки <input name='' type='' /> потрібно замінити на спеціальний компонент <Field name='' type='' component='input' />, і обгорнути вашу форму в HOC.

Основні компоненти

  • reduxForm — HOC для компонента форми, щоб передати вбудовані методи та властивості (помилки, прапорець асинхронного надсилання, функцію для обробки відправлення тощо)
  • Field — як вже говорилося, просунутий компонент <input>

Валідація та обробка помилок

Валідація може бути під'єднана напряму до кожного поля або на форму загалом. Особисто мені подобається валідація форми загалом, але це залежить від інтерфейсу.

Автори бібліотеки дають повну свободу у виборі перевірки полів, проте варто враховувати, що для помилок використовується спеціальний клас SubmitionError, який надбудовується над об'єктом errors (об'єкт зі структурою: key = ім'я поля, value = текст помилки). З ним потрібно буде працювати, якщо вирішили запускати валідацію власноруч (в зручний для вас час). Інакше, є стандартна властивість validate, що приймає об'єкт з полями і повертає об'єкт errors.

Для спрощення цієї магії з валідацією рекомендую звернути увагу на бібліотеку валідаторів, що класно вписується в логіку redux-form - redux-form-validators.

Обробка та надсилання форми

Для виконання цієї непростої задачі HOC передає в компонент спеціальну функцію handleSubmit.

const { handleSubmit } = this.props;
<form onSubmit={handleSubmit(yourOwnHandlingFunction)} />

Якщо ваша функція оброки відправлення буде асинхронною (наприклад, повертатиме Promise), то redux-form за вас оперуватиме змінною submitting, що буде змінювати своє значення з true(початок відправлення) на false (коли ланцюжок промісів виконається).

Ще один нюанс використання redux-form — ви можете самостійно згенерувати помилку полів SubmitionError під час submit (наприклад, дані не пройшли валідацію на сервері й вам повернули об'єкт з полями та помилками), що автоматично зупинить ланцюжок промісів.

Код

Щоб бути більш-менш практичними: розглянемо просту формочку авторизації з валідацією в браузері та відправленням на тестовий сервер. Як сервер використовуватимемо API сервісу https://httpbin.org — на POST запит повертається значення, що надсилаємо. Стилі (оформлення) будуть мінімальні :)

Сам компонент форми матиме вигляд:

import { reduxForm, Field } from "redux-form";
import validateForm from "../../validateForm"; // обгортка функції для перевірки полів
import { AUTH_FORM } from "../../store/constants"; // назва форми

class AuthForm extends Component {
  static propTypes = {
    handleSubmit: func.isRequired,
    submitting: bool.isRequired
  };

  onFormSubmit = data =>
    validateForm(AUTH_FORM, data)
      .then(() => {
        const { sendAuth } = this.props;

        return sendAuth(data);
      })
      .then(res => {
        const { reset } = this.props;

        reset();
      });

  render() {
    const { handleSubmit, submitting } = this.props;

    return (
      <form onSubmit={handleSubmit(this.onFormSubmit)}>
        <Field
          name="name"
          type="text"
          component={InputField}
          placeholder="name"
        />
        <Field
          name="password"
          type="password"
          component={InputField}
          placeholder="password"
        />
        <button
          type="submit"
          className="auth-form__button"
          disabled={submitting}
        >
          {submitting ? "sending..." : "Send"}
        </button>
      </form>
    );
  }
}

const WrapForm = reduxForm({
    form: AUTH_FORM
  })(AuthForm);

export default connect(null, {
  sendAuth
})(WrapForm);

А весь функціонал та структуру можна глянути на codesandbox (для валідації використовується redux-form-validators, асинхронщина redux-api-middleware).

Плюси

  • проста логіка використання, невисокий поріг входу;
  • достатньо детальна документація;
  • велика кількість прикладів;
  • автоматичне відстеження статусу обробки форми (submitting).

Мінуси

  • жорстка зв'язка з Redux;
  • значний footprint в фінальному бандлі проекту;
  • потрібно самому прописувати обробку схеми валідації полів (дивись validateForm.js в codesandbox, проте написавши раз — потім просто використовуєш повторно).

Formik

Зірка, що сходить над долиною форм. Бібліотека базується на техніці render props та перше, що впадає в око, — слоган: «Build forms in React, without the tears», хоча без сліз особисто у мене не обійшлось (можливо, у вас буде краще знайомство). Але все поступово.

Що, що, а почати використовувати Formik, насправді, дуже просто, як і обіцяв розробник. Доступно два варіанти використання: через HOC або компонент з render prop. Найпростіший для ознайомлення та використання, на мій погляд — render-props, бо всі конфігурації (підключення валідації, обробка submit) достатньо зручно організовуються в одному файлі-компоненті.

Основні компоненти

  • Formik — Компонент з усією логікою та render prop
  • withFormik — High-Order Component
  • Field — просунутий компонент <input> (аналогічно до redux-form)

Валідація та обробка помилок

Formik дає вам повну свободу вибору бібліотеки валідації. Валідація за замовчуванням проводиться на кожну зміну поля і на зміну фокуса, але це вимикається налаштуванням параметрів. В документації використовується бібліотека yup для створення валідаційних схем.

На відміну від Redux-form, ви можете налаштувати валідаційну схему без інтеграції в сам процес надсилання форми, замість вас це зробить Formik.

Для ситуації, коли валідація на фронті вдала, але сервер вирішив, що з вашими даними щось не так, можна скористатися вбудованим функціоналом setStatus або setErrors, щоб передати об'єкт помилок з середини функції надсилання форми.

Обробка надсилання форми

Тут все просто. Вам потрібно написати функцію, яка приймає values та formikBag як аргументи. Якщо з values все зрозуміло — це дані полів вашої форми, то з formikBag одразу не розберешся. formikBag — набір корисних функцій для маніпулювання станом форми, найголовніші, на мій погляд: надсилання (setSubmitting), зміни статусу (setStatus) при надходженні помилок.

Отже, використовуючи formik, потрібно самому описати функціонал зміни стану форми під час зміни етапів надсилання. Наприклад,
форма надіслала дані на сервер викликали setSubmitting(true), отримала результати — setSubmitting(false).

Код

Спробуємо повторити форму, яку зробили з redux-form. Базис той самий — redux (але в даному випадку це необов'язково), redux-api-middleware для асинхронних запитів.

Робочий компонент може виглядати наступним чином:

// в даному випадку розбив на UI частину та частину ініціалізації Formik
//InnerForm.js
const InnerForm = props => {
  const { isSubmitting } = props;

  return (
    <Form>
      <Field
        name="name"
        type="text"
        component={InputField}
        placeholder="name"
      />
      <Field
        name="password"
        type="password"
        component={InputField}
        placeholder="password"
      />
      <button
        type="submit"
        className="auth-form__button"
        disabled={isSubmitting}
      >
        {isSubmitting ? "sending..." : "Send"}
      </button>
    </Form>
  );
};

// AuthForm.js
class AuthForm extends Component {
  renderForm = props => <InnerForm {...props} />;

  handleSubmit = (values, actions) => {
    const { sendAuth } = this.props;
    const { setSubmitting } = actions;

    setSubmitting(true);
    sendAuth(values).then(() => {
      console.log(values);
      setSubmitting(false);
    });
  };

  render() {
    return (
      <Formik
        {...this.props}
        validateOnBlur={false}
        validateOnChange={false}
        onSubmit={this.handleSubmit}
        validationSchema={formValidationRules[AUTH_FORM]}
        render={this.renderForm}
      />
    );
  }
}

export default connect(null, {
  sendAuth
})(AuthForm);

Зібраний проект з формою можна поклацати на codesandbox.

Плюси

  • невеликий розмір в бандлі;
  • валідація;
  • використання сучасного підходу render prop (50/50);
  • повний контроль над станами (setSubmitting) та помилками (setStatus).

Мінуси

  • необхідність створювати або два окремі компоненти (щоб розділити функціонал): InnerForm (що містить UI частину) та FormWrapper (що матиме логіку та використовуватиме Formik компонента), або об'єднувати це в один великий файл (на мій погляд, для перевірки це не дуже зручний варіант);
  • використання методу render-prop додає свої ігри з рендерингом (варто мінімізувати використання анонімних функцій)

Зберемо все до купи

Табличка характеристик

Відомості redux-form formik
дата створення Серпень 2015 Червень 2017
GitHub's start (травень 2018) 9K 6K
minified + gzipped size 27.4 kB 10.7 kB
завантажень з npm (березень-квітень 2018) 3 914 118 446 073

На мій погляд, якщо у вас невеликі форми й вам не сподобалася техніка render props, або просто не хочете заморочуватися, або поекспериментувати з нею, щоб зрозуміти основні моменти в рендерингу та обробці форм в React, то цілком логічно використати Redux-form. Якщо ви впевнено почуваєтеся в React екосистемі, варто спробувати Formik з його достатньо хорошою документацією та кількістю прикладів.

Почитати:

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.6K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація