Autosave with React Hook Form Controlled Inputs
React Hook Form's controlled inputs require special handling for autosave. This guide shows you how to integrate Form Guardian properly.
The Challenge
React Hook Form can work in both controlled and uncontrolled modes. When using controlled inputs, you need to restore values using setValue rather than direct DOM manipulation.
Basic Setup
import { useForm } from 'react-hook-form';
import { useFormAutosave } from '@form-guardian/react';
import { useEffect } from 'react';
interface FormData {
name: string;
email: string;
message: string;
}
function ControlledForm() {
const { register, handleSubmit, setValue, getValues, watch } = useForm<FormData>({
defaultValues: {
name: '',
email: '',
message: '',
},
});
const { formRef, restoreValues, clearDraft } = useFormAutosave('controlled-form', {
autoRestore: false, // We'll restore manually for controlled inputs
});
// Restore draft on mount
useEffect(() => {
restoreValues(
(field, value) => {
setValue(field as keyof FormData, value, { shouldValidate: false });
},
() => getValues()
);
}, []);
const onSubmit = async (data: FormData) => {
// Submit logic
await clearDraft();
};
return (
<form ref={formRef} onSubmit={handleSubmit(onSubmit)}>
<input {...register('name', { required: true })} />
<input {...register('email', { required: true })} type="email" />
<textarea {...register('message', { required: true })} />
<button type="submit">Submit</button>
</form>
);
}
Key Points
1. Use shouldValidate: false When Restoring
setValue(field, value, { shouldValidate: false });
This prevents validation errors from triggering when restoring draft values.
2. Manual Restoration
autoRestore: false, // Disable auto-restore
Controlled inputs need manual restoration using restoreValues to properly update React state.
3. Get Current Values
restoreValues(
(field, value) => setValue(field, value),
() => getValues() // Provide current values
);
The second parameter provides current form values for Form Guardian to track.
Advanced: With Validation
function ControlledFormWithValidation() {
const {
register,
handleSubmit,
setValue,
getValues,
formState: { errors, isDirty }
} = useForm<FormData>({
defaultValues: {
name: '',
email: '',
message: '',
},
});
const { formRef, restoreValues, clearDraft, hasDraft } = useFormAutosave(
'controlled-form',
{ autoRestore: false }
);
useEffect(() => {
if (hasDraft) {
restoreValues(
(field, value) => {
setValue(field as keyof FormData, value, {
shouldValidate: false,
shouldDirty: true, // Mark as dirty after restore
});
},
() => getValues()
);
}
}, [hasDraft]);
const onSubmit = async (data: FormData) => {
await submitForm(data);
await clearDraft();
};
return (
<form ref={formRef} onSubmit={handleSubmit(onSubmit)}>
<div>
<input
{...register('name', {
required: 'Name is required',
minLength: { value: 2, message: 'Min 2 characters' }
})}
/>
{errors.name && <span>{errors.name.message}</span>}
</div>
<div>
<input
{...register('email', {
required: 'Email is required',
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email'
}
})}
type="email"
/>
{errors.email && <span>{errors.email.message}</span>}
</div>
<button type="submit">Submit</button>
</form>
);
}
With Watch Mode
If you're using watch for real-time updates:
function ControlledFormWithWatch() {
const { register, handleSubmit, setValue, getValues, watch } = useForm<FormData>();
const { formRef, restoreValues, clearDraft } = useFormAutosave(
'controlled-form',
{ autoRestore: false }
);
const watchedValues = watch(); // Watch all fields
useEffect(() => {
restoreValues(
(field, value) => setValue(field as keyof FormData, value),
() => getValues()
);
}, []);
// Watch values are automatically tracked by Form Guardian
return (
<form ref={formRef} onSubmit={handleSubmit(onSubmit)}>
<input {...register('name')} />
<input {...register('email')} type="email" />
</form>
);
}
Best Practices
- Always use
shouldValidate: falsewhen restoring to avoid validation errors - Set
shouldDirty: trueif you want restored values to mark form as dirty - Disable auto-restore for controlled inputs and restore manually
- Clear draft after successful submission to prevent stale data
- Handle errors gracefully when restoration fails
Common Pitfalls
❌ Don't Use autoRestore: true with Controlled Inputs
// ❌ Wrong - won't work properly with controlled inputs
const { formRef } = useFormAutosave('form', {
autoRestore: true, // This tries to set DOM values directly
});
✅ Do Use Manual Restoration
// ✅ Correct - manual restoration for controlled inputs
const { formRef, restoreValues } = useFormAutosave('form', {
autoRestore: false,
});
useEffect(() => {
restoreValues(setValue, getValues);
}, []);
Next Steps
- 📖 React Hook Form Integration - Full integration guide
- 🍳 Multi-Step Forms - Handle multi-step forms
- 📚 API Reference - Complete API documentation