🏁 Final Form: Дорога к клетчатому флагу

Ridergg
devSchacht
Published in
7 min readDec 21, 2017

--

Перевод статьи Erik Rasmussen: Final Form: The road to the checkered flag

Два с половиной года назад, примерно через месяц после незабываемого запуска Деном Абрамовым Redux на React Europe 2015, я выпустил скромную библиотеку, которая управляла состоянием в Redux, имя которой Redux-Form и за небольшой период она стала весьма популярна.

На момент написания этот статьи (конец 2017 года), Redux-Form имеет более 8000 звезд в Github и 800 тысяч ежемесячных загрузок в NPM. По моим недавним подсчетам, основываясь исключительно за скачиваниях из NPM, более 50% проектов на React используют Redux и около 25% проектов используют Redux-Form, так что 11% из 7 миллионов проектов, скачивающих React, скачивают и Redux-Form.

С увеличением популярности и ростом использования библиотеки, я получил огромное количество обратной связи. У каждого был свой пользовательский опыт и каждый просил немного другое поведение в Redux-Form. Я сделал все возможное, чтобы учесть все пожелания и добавил новую функциональность, о которой я бы сам никогда не подумал. Я много думал о формах и управлением состояния форм.

Проблемы с Redux-Form

За эти годы я услышал четыре основные жалобы на Redux-Form. По какой-то причине, в отличие от большинства других жалоб, я больше всего слышал их лично на конференциях, чем в Twitter или Github или Stackoverflow. Они представлены далее с эмоджи, показывающими уровень отчаяния:

1. 🙁 Эта библиотека только для React?

«Redux работает с Angular и другими фреймворками, очень плохо, что Redux-Form этого не может.»

2. ☹️ Должен ли я использовать Redux?

«Мне действительно нужно решение для формы, но мой проект не использует Redux». Кроме того, все сливки React-сообщества, в том числе и изобретатели Redux, говорят, что Redux не лучшее место для хранения данных формы. Упс.

3. 😖 Могу ли я использовать инлайновые функции рендеринга?

«Почему мой input теряет фокус при первом нажатии». Потому что Redux асинхронно обновляет как ваш input так и окружающие компоненты форм (потому что сейчас dirty === true), и потому, что ваша инлайновая функция рендеринга !== предыдущей,
бла-бла-бла 😢

4. 😭 Это более чем 26 кб в gzip? Ни за что!

«Размер бандла, бро. Я слежу за ним»

Преждевременная оптимизация

Поскольку 5-я версия Redux-Form стала очень популярной, самая большая жалоба была в том, что она была медленной. Все из за того, что перерисовка всей формы вызывалась при каждом нажатии, поскольку значения были предоставлены компоненту формы.

В начале 2016 года я решил эту проблему, полностью переписав его с нуля (6-я версия), создав компонент <Field />, который обновлялся только при изменении его состояния, но не обновлялся при изменении состояния другого поля. Это очень сложно, потому что теперь вся <Form/> получает лишь несколько свойств (props) и поэтому многим пришлось пробрасывать в connect() набор селекторов, чтобы получить другие поля состояния формы, которые не предоставлялись по умолчанию.

Вполне возможно, что страх перерендера всей формы, который привёл к созданию Redux-Form v6, не обоснован, но независимо от этого, замечает ли пользователь это или нет, бесполезная перерисовка части вашего дерева замедляет работу. Я решил, что мой следующий релиз Redux-Form должен иметь встроенную логику «вызов функции рендера только там, где это действительно нужно», которая большинству людей никогда не понадобится, но это позволит точно настраивать сложные формы.

Решение

Спустя некоторое время, в начале 2017 года, у меня возникла идея создать «движок управления состоянием форм», который был бы полностью независим от React или Redux, был бы просто чистым JavaScript. Моя первая попытка заключалась в использовании Immutable.js, я хотел использовать его мощь для быстрой проверки «это дерево изменилось?». Я был уже на полпути (за окном май), пока не упомянул об этом David Khourshid, автору данной библиотеки. Его реакция на использование Immutable.js (15.3 кб в gzip) была негативной и это заставило меня переосмыслить мой подход: смог бы я сделать это без помощи сторонних библиотек? В августе, во время отпуска, я начал писать библиотеку управления состоянием формы без использования сторонних зависимостей.

Переход к Flow

В обычном случае я бы писал библиотеку изнутри, от ядра до публичного API, но на этот раз я сначала определил API, даже написав некоторые тесты, прежде чем реализовывать основные функции. Написав API с помощью Flow, я смог отлавливать ошибки и убедиться, что внутренние компоненты удовлетворяют моему API. Это было похоже на парное программирование с раздражающим партнером, который постоянно кричал: «На самом деле, я здесь ожидаю строку!», но в хорошем смысле! Flow потрясающий. 😍

Засорение пространства имен

Прежде чем перейти к финишной черте данного поста, я хотел бы поговорить о процессе выбора имени библиотеки. Я, вероятно, пробовал сотню разных имен, включающих слово «form», прежде чем найти то, которое не использовано в NPM. Очень много неиспользуемых, незавершенных, незадокументированных библиотек, которые давным-давно не обновлялись, а другие имена были попросту засквоченны пустыми библиотеками — тактика, к которой я сам был вынужден прибегнуть для резервирования имени моей библиотеки до запуска. И тут я понял, что зарезервировать хорошие имя библиотеки на NPM, это как зарезервировать крутой домен на .com.

Говоря об пространстве имен …Emojispace! После просмотра успешных библиотек 2017 года, использующих эмоджи в названии, таких как Styled Components 💅, Glamorous 💄, и Downshift 🏎️, я принял решение, что моя библиотека не будет исключением. Я очень нервничал, что кто-то может украсть мой эмоджи, который я выбрал несколько месяцев назад перед запуском, так что это очень меня мотивировало к быстрому релизу!

Ну а теперь настало время анонсировать…

🏁 Final Form

Сегодня я запускаю новую библиотеку, названную 🏁 Final Form. Она основана на паттерне Наблюдатель (Observer), где observers могут подписаться на получение обновлений изменений формы или поля. Каждый observer должен явно указать, об изменении каких частей состояния он хочет получать уведомления, и он будет вызван только тогда, когда части, на которые он подписался, будут изменены.

Библотека не имеет зависимостей, является независимой от фреймворков и весит 3,5 кб в gzip 💥

🏁 React Final Form

Конечно, Final Form должен был выйти с библиотекой-компаньоном
🏁 React Final Form, которая является небольшой оберткой вокруг
🏁 Final Form, что позволяет использовать ее с React. И когда я говорю небольшой, то подразумеваю 💥 2.2 кб в gzip 💥, что вместе с React на выходе дает 5.7 кб в gzip. Она не имеет зависимостей, и для её работы нужны только две библиотеки: 🏁 Final Form и React.

Проблемы с Redux-Form: ✅ Решено!

  1. 🙂 Эта библиотека только для React?

Нет! Ничто не останавливает тебя от того, чтобы имплементировать обертку для 🏁 Final Form в Angular, Ember, Vue, Preact, Elm, ReasonML, TheNextHotness™, и так далее.

2.😊 Должен ли я использовать Redux?

Нет! Состояние хранится внутри 🏁 Final Form, а React компоненты знают, когда необходимо инициировать перерерисовку путём вызова setState() в момент изменения состояния.

3. 😀 Могу ли я использовать встроенные функции рендеринга?

Конечно вы можете! Перефразирую слова одного выдающегося руководителя сообщества React:

“OMG!! Render functions! RENDER FUNCTION ALL THE THINGS!!!11one11”

- Michael Jackson

Компоненты <Form /> и <Field /> используют три способа рендеринга, которые я смиренно украл из удивительной конкурирующей библиотеки Formik Джареда Палмера. Великолепная идея. Вы можете либо передать поле component, либо поле render, либо функцию рендеринга в свойсте children (смотрите документацию)

4. 😍 Всего 5.7 кб в gzip? 21% от размера Redux-Form? ЗДОРОВО!

Дёшево и сердито. 😎

На что это похоже?

Люди, знакомые с синтаксисом Redux-Form, должны чувствовать себя как дома, на самом деле многие пользовательские библиотеки компонентов, адаптированные для Redux-Form, должны Просто Работать™ с 🏁 React Final Form. В конечном итоге я напишу Руководство по Миграции, но пока я до этого не добрался.

Планы развития

🏁 Final Form пока ещё не имеет полный паритет с Redux-Form. Например, при запуске, мы упустили концепцию <FieldArray /> для редактирования массива значений и <Fields/> для редактирования нескольких полей в одном компоненте. (Исправлено: Функциональность <FieldArray/> была выпущена через 7 дней после этого поста).

Кроме того, у него нет некоторых более эзотерических функций, таких как autofill. Я планирую ответить тем, кто желает большего: «Нет, я не буду реализовывать вашу специфичную функцию» с этой библиотекой, чтобы свести к минимуму ее сложность. Я подозреваю, что в большинстве редких случаях, которые ранее требовали новый action в Redux, можно было обойтись state в React.

Я с нетерпением жду, когда кто то захочет использовать 🏁 Final Form в других фреймворках с помощью обертки или напишет другую реализацию 🏁React Final Form. Один совет: вам придется рано вставать, если вы захотите быть доминирующей на рынке библиотекой (смотрите 1 абзац).

Итого

Я не могу дожидаться фидбека от коммьюнити. Я многому научился у вас всех в свои годы благодаря Redux Form. ❤️

Я планирую продолжать поддерживать Redux Form. Если вы создали огромное корпоративное приложение с помощью Redux Form — не волнуйтесь, я никуда не денусь. Но возможно вы попробуете создать следующее приложение с помощью 🏁 Final Form? ;)

🙏 Спасибо за чтение. Пойдите, установите, запустите и попробуйте! 🎉

--

--