Capítulo 27 de 39
useController is the hook that powers Controller — it lets you build your own reusable controlled-input component with full access to field (value/handlers/ref), fieldState (per-field invalid/touched/dirty/error), and formState.
FormProvider, otherwise required — the control object from useForm.register's second argument (required, min, max, minLength, maxLength, pattern, validate).false; avoid true together with useFieldArray since unregister fires on every unmount/remount/reorder there.true for useController (unlike useWatch/useFormState, which default exact to false) — controls whether the name subscription is an exact match.onChange reports the new value, onBlur reports interaction, ref lets RHF focus the input on a validation error.undefined — either set it at the field level or provide defaultValues at useForm; an initially-undefined field.value makes the input start uncontrolled and React will warn when it later becomes controlled.function Input({ control, name }) {
const {
field,
fieldState: { invalid, isTouched, isDirty },
} = useController({ name, control, rules: { required: true } })
return (
<TextField
onChange={field.onChange}
onBlur={field.onBlur}
value={field.value}
name={field.name}
inputRef={field.ref}
/>
)
}
TextField (MUI) that doesn't forward a native ref the way RHF's register expects.| Prop | Responsibility |
|---|---|
onChange | send the new value back to RHF |
onBlur | report the field was touched/blurred |
value | set the input's current/initial value |
ref | let RHF focus the input on a validation error (requires React.forwardRef support, or an equivalent like MUI's inputRef) |
name | give the input a unique registered name |
register on the same field a useController already manages: double-registers the input; use field directly, never both.useController calls in one component without renaming field: each call subscribes independently, and unrenamed destructures collide; either rename each destructure (field: input, field: checkbox) or use Controller when you need several controlled fields in one component.field.onChange with local useState incorrectly: it's fine to keep local UI state alongside field, but always still call field.onChange so RHF's own state stays the source of truth for validation/submission.useController gives you the Controller component's exact mechanics as a hook — reach for it when building a reusable controlled-input component.field already carries the full registration.useController per component; use Controller (or renamed destructures) when you need more than one controlled field together.control object passed in comes from.