Form đăng ký quá dài? Chia nhỏ nó ra thành nhiều bước (Steps) để user đỡ ngán.
1. Kiến trúc State (State Lifting)
Sai lầm phổ biến: Mỗi Step là một Form riêng biệt. -> Dữ liệu bị phân mảnh. Khó submit ở bước cuối.
Giải pháp đúng: Một useForm đặt ở Component Cha. Các Step chỉ là Component Con nhận register/control từ cha.
function WizardForm() {
const { register, trigger, handleSubmit } = useForm<WizardData>();
const [step, setStep] = useState(1);
const nextStep = async () => {
// 🔥 Magic: Validate CHỈ những trường của bước hiện tại
let isValid = false;
if (step === 1) isValid = await trigger(["firstName", "lastName"]);
if (step === 2) isValid = await trigger(["email", "phone"]);
if (isValid) setStep(s => s + 1);
};
return (
<form onSubmit={handleSubmit(finalSubmit)}>
{/* Chỉ hiển thị Step hiện tại */}
{step === 1 && <Step1 register={register} />}
{step === 2 && <Step2 register={register} />}
{step < 3 && <button onClick={nextStep}>Next</button>}
{step === 3 && <button type="submit">Finish</button>}
</form>
)
}2. Validate từng phần (trigger)
Hàm trigger("field_name") của RHF cực kỳ hữu dụng ở đây.
Nó chạy validation Zod cho đúng field bạn chỉ định.
- Nếu Step 1 user nhập sai ->
triggertrả về false -> Không cho next. - Ngăn chặn việc sang Step 3 rồi mới báo lỗi ở Step 1.
3. Bảo lưu dữ liệu (Persist Data)
Khi user bấm Back từ Step 2 về Step 1, dữ liệu Step 1 phải còn nguyên.
Vì ta dùng kiến trúc State Lifting (useForm ở cha), dữ liệu luôn nằm trong bộ nhớ của Cha.
Việc unmount Step1 rồi mount lại không làm mất dữ liệu trong RHF Store.
4. UX & Animation
Để Wizard xịn sò hơn, hãy thêm Animation trượt ngang (Slide) với Framer Motion.
<AnimatePresence mode="wait">
<motion.div
key={step}
initial={{ x: 50, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
exit={{ x: -50, opacity: 0 }}
>
{step === 1 && <Step1 ... />}
</motion.div>
</AnimatePresence>Kết luận
Multi-step Form không khó về kỹ thuật (vẫn là 1 form to). Nó chỉ khó về Validation Logic (đảm bảo step trước đúng thì mới cho qua step sau). Hàm trigger của RHF giải quyết trọn vẹn vấn đề này.