← Все статьи

Декларативные зависимости полей в формах React

Вместо спагетти из useEffect — конфиг зависимостей, паттерн «наблюдатель» и мелкозернистая реактивность на MobX.

Декларативные зависимости полей в формах React
Содержание

Коротко

На Dev.to описан подход к сложным формам на React: зависимости полей описывают в конфигурации, а не в цепочках useEffect.

Поля «подписываются» друг на друга (паттерн «наблюдатель»). MobX даёт мелкозернистую реактивность — перерисовывается только то, что реально изменилось.

Прототип фармацевтической формы с десятками полей заметно упростил код. Правила стали читаемыми даже для коллег вне клиентской части.

Что произошло

В корпоративных приложениях формы редко бывают «тремя полями ввода»: регистрация, оформление заказа, опросы, ввод данных.

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

Стандартный путь в ReactuseEffect на каждое изменение. На пяти полях это ещё читается.

На тридцати с перекрёстными цепочками получается спагетти: бесконечные циклы, хрупкие тесты, страх добавить ещё одно правило.

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

Автор вспоминает старый XML-каркас интерфейса, где зависимость писали декларативно (dependsOn, loadFrom, clearOnChange) без ручных обработчиков.

Современный React декларирует разметку, но логику связей полей чаще пишут императивно. Альтернатива в статье — массив модели формы.

У поля явно указаны subscribesTo / subscribesOn, откуда грузить варианты (loadDataFrom) и когда показывать (visibleWhen). Читаешь сверху вниз — видишь контракт формы.

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

Чтобы это работало, нужен механизм «поле A изменилось → заинтересованные поля узнали». Наивный обход всех полей после каждого нажатия клавиши на 30+ полях тормозит.

Решение — паттерн «наблюдатель» плюс хранилище с мелкозернистой реактивностью. Автор сравнивает Redux/Zustand с MobX и собирает функциональные хранилища без классов.

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

Автор честно пишет, что отладка движка сложнее, чем трассировка одного явного useEffect. Зато контракт формы перестаёт быть размазанным по компонентам.

Почему это важно

Сложные формы — не ниша фармы. Те же паттерны в финансовых сервисах, госуслугах, интернет-торговле и внутренних панелях.

Императивные эффекты плохо масштабируются как документация. Разработчик серверной части не «прочитает» бизнес-правила из десятка useEffect.

Конфиг как данные проще рецензировать, переносить между режимами просмотр/редактирование и частично генерировать с сервера.

Когда правила живут в данных, их проще версионировать вместе с API и покрывать тестами без монтирования всего дерева компонентов.

Есть и цена: свой формат конфигурации, кривая обучения, сложнее отладка движка подписок. Часть правил всё равно уйдёт в императивный код.

Для простой формы с одной зависимостью «страна → город» накладные расходы библиотеки не окупятся. Подход окупается на больших формах с повторяющимися цепочками.

Подход Как описаны связи Типичный риск
Цепочки useEffect В коде эффектов Скрытые циклы, хрупкие правки
Конфиг + подписки В данных модели Кривая обучения движку
Только библиотека значений Частично в коде Связи всё равно вручную

На практике

Если форма уже разрослась до «эффектов, которые дергают эффекты», сначала опишите зависимости на бумаге.

Часто выясняется, что половина правил — это две повторяющиеся операции: показать поле и обновить список вариантов.

Имеет смысл сначала описать эти два действия в конфиге, а уже потом решать, нужна ли отдельная библиотека или достаточно тонкого слоя поверх текущих форм.

  1. Выпишите граф: какое поле на что влияет (видимость, перезагрузка списка, сброс значения, правило проверки).
  2. Выделите два частых действия: показать/скрыть и обновить данные зависимого списка.
  3. Для больших форм оцените мелкозернистую реактивность (MobX или аналог).
  4. Тестируйте правила подписок как чистые данные — отдельно от монтирования компонентов React.
  5. Не отбрасывайте Formik / React Hook Form: они хорошо держат значения и отправку; декларативные зависимости — про связи.

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

Если команда уже сидит на Formik или React Hook Form, не выкидывайте их ради «чистоты» — добавьте слой зависимостей поверх, а не вместо.

Итог

Декларативные зависимости полей не отменяют useEffect навсегда.

Они переносят «кто на кого влияет» из императивного шума в читаемый конфиг. На прототипе фармацевтической формы это сократило объём логики и сделало правила понятнее команде.

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

Если форма ещё простая — оставьте явные эффекты. Если граф зависимостей уже не помещается в голове одного разработчика, конфиг и подписки окупаются быстрее, чем ещё один слой useEffect.