Performance
For most forms you can read formState directly and never think about
performance. The thing to know for large forms: reading formState re-renders a
component on any state change. El Form gives you selective subscriptions so a
component only re-renders when the slice it cares about changes.
useField — subscribe to one field
The simplest optimization. useField(path) returns { value, error, touched }
for a single field and re-renders only when that field changes — not when other
fields do:
import { useField } from "el-form-react-hooks";
function EmailField() {
const { value, error, touched } = useField("email");
// re-renders only when "email" value/error/touched changes
return (
<>
<input value={value} readOnly />
{touched && error && <span>{error}</span>}
</>
);
}
This pairs naturally with FormProvider, so each field component subscribes to
its own slice instead of the whole form.
useFormSelector — subscribe to a derived slice
When you need something more specific than one field — a computed value, or a
couple of fields together — use useFormSelector(selector, equality?). The
component re-renders only when the selected value changes:
import { useFormSelector, shallowEqual } from "el-form-react-hooks";
// single derived value
const email = useFormSelector((s) => s.values.email);
// multiple values — pass shallowEqual so a new object each render
// doesn't force a re-render
const { first, last } = useFormSelector(
(s) => ({ first: s.values.firstName, last: s.values.lastName }),
shallowEqual
);
shallowEqual is exported for exactly this case: selectors that return a fresh
object/array each call but whose contents usually haven't changed.
Why this matters
A form that reads formState in 50 field components will re-render all 50 on
every keystroke. The same form built with useField re-renders only the field
being typed into. The difference is invisible on a login form and very visible
on a large settings page.
Other tips
- Validate on the right event.
mode: "onSubmit"(the default) does the least work;"onChange"validates every keystroke. UseonChangeonly where live feedback matters. - Debounce async validation. Server checks should use
asyncDebounceMsso you don't fire a request per keystroke — see Async Validation. - Memoize custom field components (
React.memo) when they take stable props, so a parent re-render doesn't cascade.
Next steps
- Form State — what's in
formState - Component Reusability — building field components
- Async Validation — debouncing server checks