useListener
This library is build upon the fact that only the things that change should rerender, for example: when the name field changes, only the inputs that use the name field will rerender.
The built-in form input (<Field/>
) implements this by listening for changes only on its specified field. You can also use multiple inputs on the same field (they will the synchronized).
The useListener hook listens for changes on a specific form field. It behaves like useState. Because this hooks causes a rerender, you shouldn’t use this hook in the same component as the form it is using (causes the whole form to rerender). You should always create a new component which contains the hook and use that. Or use the Listener
component, which wraps the useListener
hook for you.
This hook must be called, unconditionally, at the start of your component, just like the normal React hooks.
To listen for all form fields at once, use the useAnyListener
hook instead.
✔️ Right usage
interface Bread {
color: string;
size: number;
}
// Always create a new component when using listener hooks
function BreadSizeVisualizer(props: { form: FormState<Bread> }) {
// Listen for changes on the size field, behaves exactly like useState
const { value } = useListener(props.form, "size");
// State change only rerenders this component, not parent
return <span>{value > 50 ? "long" : "short"}</span>;
}
function BreadForm() {
const form = useForm<Bread>({ color: "brown", size: 58 });
return (
<form>
<Field form={form} type="text" name="color" />
<Field form={form} type="number" name="size" />
<BreadSizeVisualizer form={form} />
</form>
);
}
❌ Wrong usage (causes form rerender)
function BreadForm() {
const form = useForm<Bread>({ color: "brown", size: 58 });
// Causes whole form to rerender! Is not ok!
const { value } = useListener(form, "size");
return (
<form>
<Field form={form} type="text" name="color" />
<Field form={form} type="number" name="size" />
<span>{value > 50 ? "long" : "short"}</span>
</form>
);
}
✔️ Right usage using Listener
instead of useListener
function BreadForm() {
const form = useForm<Bread>({ color: "brown", size: 58 });
return (
<form>
<Field form={form} type="text" name="color" />
<Field form={form} type="number" name="size" />
{/* Use the Listener component, which wraps the useListener hook in a component. Does NOT cause a form rerender! */}
<Listener form={form} name="size" render={({ value }) => <span>{value > 50 ? "long" : "short"}</span>} />
</form>
);
}
Parameters
useListener(form, name)
form
(required): the form which contains the field to listen to.name
(required): the name of the field to listen for.
Return value
Returns a FormField
instance containing the following fields, which you can destruct:
return {
value, // Current value of the listened field
defaultValue, // Default value of the listened field
setValue, // Function to update the value
dirty, // True if the field is modified (if not default value anymore)
error, // The error on this field
state, // The state of the form (contains isSubmitting)
form // The form this field belongs to (FormState instance)
};
// Example usage
const { value, setValue, error } = useListener(form, "fieldname");