--- title: React.memo Cannot Stop Context-Driven Re-renders Under FormProvider impact: MEDIUM impactDescription: replaces a memo pass that has no effect with isolation that does tags: sub, FormProvider, memo, useFormContext, isolation --- ## React.memo Cannot Stop Context-Driven Re-renders Under FormProvider The instinct when a `FormProvider` tree re-renders too much is to wrap the heavy children in `React.memo`. It does nothing here. `React.memo` compares props; a component that calls `useFormContext()` is a context consumer, and a context value change re-renders it regardless of whether its props were equal. `FormProvider` already memoizes its own value, but `formState` is one of that memo's dependencies, so the value's identity does change on every form-state update โ€” and every `useFormContext()` consumer under it re-renders. Wrapping in `memo` therefore buys nothing for the components you were worried about, and costs a comparison on every render for the ones you weren't. **Incorrect (memo on a context consumer โ€” still re-renders on every form-state update):** ```typescript const AddressSection = React.memo(function AddressSection() { const { register, control } = useFormContext() const { errors } = useFormState({ control }) // Consumes context; memo is bypassed return (
{errors.street && {errors.street.message}}
) }) ``` **Correct (subscribe to the narrowest slice, so the re-render is cheap and local):** ```typescript function AddressSection() { const { register, control } = useFormContext() return (
(errors.street ? {errors.street.message} : null)} />
) } ``` `React.memo` is still worth reaching for on a genuinely expensive child that takes plain props and does **not** read form context โ€” a chart, a map, a large static list rendered as a sibling of the form. The distinction is whether the component consumes context at all. Reference: [FormProvider](https://react-hook-form.com/docs/formprovider) ยท [useFormState](https://react-hook-form.com/docs/useformstate)