Replies: 1 comment
|
Honestly, the headless The callback form of I’d split the subscriptions explicitly: function AutoSave<T extends FieldValues>({
control,
onSave,
delay = 1000,
}: {
control: Control<T>
onSave: (values: T) => void | Promise<void>
delay?: number
}) {
const values = useWatch({ control }) as T
const { isDirty, dirtyFields } = useFormState({ control })
useEffect(() => {
if (!isDirty || Object.keys(dirtyFields).length === 0) return
const id = setTimeout(() => {
void onSave(values)
}, delay)
return () => clearTimeout(id)
}, [values, isDirty, dirtyFields, delay, onSave])
return null
}Then render That subscribes to exactly the pieces autosave cares about, without resubscribing the whole form every time |
|
The bug is caused by a stale closure over Root Cause:
|
Uh oh!
There was an error while loading. Please reload this page.
I made a custom hook
useAutoSavewhich is used on my form component.bank-create-update-form.tsx- This form handles both create mutation & update mutation. Everytime successful create/update mutation I reset the form via form.reset(dataFromMutationResponse)use-auto-save.tsI always rely on the form state since I provide defaultValues via props (initial data), and I call form.reset() after a successful response.
The issue I’m facing is that when I type a single character into an input:
Typing 'A' → does not trigger autoSave
Typing 'B' (making it "AB") → autoSave is triggered
So the first input change isn’t being detected properly.
simplescreenrecorder-2025-09-24_13.57.26.mp4
As a temporary workaround, I created a headless component that uses useWatch. Placing it as a separate component prevents unnecessary re-renders of the entire form, and it works correctly.
However, I’d like to know if there’s a way to make form.watch handle this properly without needing to rely on external packages to check isDirty/dirtyFields.
Thanks!
All reactions