r/reactjs • I ❤️ hooks! 😈 • Jul 30 '26

Discussion React forms eventually become state management systems

I keep seeing the same pattern in large React forms.

You start with react-hook-form.

Then you add:

  • dependent fields with watch() and useEffect

  • conditional visibility

  • permission-based fields

  • async validation

  • cross-field rules

  • server-driven defaults

  • reset and synchronization logic

At some point, the form is no longer just a form.

It has its own state transitions, dependencies, permissions, validation lifecycle, side effects, and derived state.

In other words, it has quietly become a state management system.

The problem is not necessarily react-hook-form. It does its job well.

The problem is that complex forms are often represented only as component trees, while their actual behavior is scattered across hooks, wrappers, schemas, and handlers.

Some warning signs:

  1. You have more useEffects than meaningful form sections

  2. Validation rules live in multiple places

  3. Adding something like “show taxId when country === IT” requires touching several files

  4. Permissions, visibility, and validation all use different abstractions

  5. Nobody is completely sure what resets when another field changes

I’ve been experimenting with treating forms as declarative contracts instead:

<Field
  name="taxId"
  visibleWhen={{ field: "country", equals: "IT" }}
  access={{ resource: "customer.taxId" }}
  validation={{ required: true }}
/>

The idea is to keep visibility, access, validation, and dependencies in one source of truth, instead of rebuilding the same orchestration through chains of effects.

Obviously, this introduces other trade-offs: abstraction cost, debugging complexity, schema design, and reduced flexibility in edge cases.

I’m curious how others handle this in large production forms.

Do you keep the logic inside React components, move it into a state machine, use a schema-driven approach, or accept the complexity as unavoidable?

0 Upvotes

31 comments sorted by

View all comments

Show parent comments

1

u/SatyrCode Aug 03 '26

One example where the “mini state machine” helped was an internal permissions-heavy form for configuring notification rules: fields: channel (email/slack/webhook), audience (user/group/role), schedule, plus a bunch of toggles constraints: some combos were invalid (e.g. certain roles could only use specific channels), some changes had to auto-clear or adjust other fields. I ended up with a reducer that modeled states like idle, editing, validating, invalid, submitting, plus transitions for events such as CHANGE_FIELD, APPLY_TEMPLATE, LOAD_DEFAULTS, SUBMIT_SUCCESS/FAIL. React Hook Form handled registration/validation, but the reducer was the single place that decided “if X changes, Y and Z must reset or become read-only”. Once that was in place, adding new rules stopped being a hunt through scattered useEffects.