Kịch bản ác mộng: User đang viết bài Blog dài 2000 chữ. Mất điện. Máy sập. Mở lại -> Form trắng tinh. -> User đập máy.
Là 1 Dev có tâm, hãy làm Auto-Save.
1. Cơ chế Auto-Save với LocalStorage
Ý tưởng:
- Lắng nghe mọi thay đổi của Form (
watch). - Lưu vào
localStorage. - Khi User vào lại trang -> Check
localStorage-> Nếu có thì điền lại (reset).
2. Implementation
import { useEffect } from "react";
import { useForm } from "react-hook-form";
const STORAGE_KEY = "draft_post_v1";
export function BlogForm() {
const { register, watch, reset } = useForm();
// 1. Load Draft khi mới vào
useEffect(() => {
const saved = localStorage.getItem(STORAGE_KEY);
if (saved) {
const parsed = JSON.parse(saved);
// Hỏi user trước khi restore (UX tốt)
if (confirm("Tìm thấy bản nháp cũ. Bạn có muốn khôi phục không?")) {
reset(parsed);
}
}
}, [reset]);
// 2. Lắng nghe thay đổi và Save
useEffect(() => {
const subscription = watch((value) => {
// Lưu toàn bộ form value vào storage
localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
});
return () => subscription.unsubscribe();
}, [watch]);
// 3. Xóa Draft khi Submit thành công
const onSubmit = async (data) => {
await saveToAPI(data);
localStorage.removeItem(STORAGE_KEY); // Clean up
};
// ...
}3. Tối ưu hóa (Debounce)
Việc ghi vào localStorage là đồng bộ (Sync) và khá chậm (chậm hơn RAM). Nếu form lớn mà ghi mỗi lần gõ phím -> Lag.
Giải pháp: Debounce. Chỉ save sau khi user ngừng gõ 1 giây.
Sử dụng use-debounce hoặc lodash.debounce.
// Custom Hook
function useAutoSave(data, key, delay = 1000) {
const debouncedData = useDebounce(data, delay);
useEffect(() => {
localStorage.setItem(key, JSON.stringify(debouncedData));
}, [debouncedData, key]);
}Kết luận
Tính năng nhỏ nhưng "cứu mạng" User.
Đặc biệt quan trọng với các form dài như: Viết bài, Đăng ký hồ sơ, Checkout.
Hãy nhớ thêm logic Versioning cho key (vd: form_v2), đề phòng trường hợp bạn đổi cấu trúc form nhưng user vẫn load lại data cũ của form v1 -> Lỗi.