
Xây dựng biểu mẫu động (Dynamic Forms) trong React và Next.js: Khi nào nên dùng Component hay Schema?
Khám phá hai hướng tiếp cận xây dựng biểu mẫu động trong React: sử dụng React Hook Form kết hợp Zod cho các form UI-driven và SurveyJS cho các form logic phức tạp, hướng dữ liệu (schema-driven).
Bài viết được dịch và tổng hợp từ tin tức gốc. Bạn có thể đọc bài viết gốc bằng tiếng Anh tại đây.
Điểm tin nhanh:
- Biểu mẫu (forms) trong React thường được xử lý bằng component-driven (React Hook Form + Zod), nhưng sẽ trở nên cồng kềnh khi logic nghiệp vụ phức tạp.
- Cách tiếp cận Schema-driven (SurveyJS) coi biểu mẫu là dữ liệu JSON, giúp tách biệt logic nghiệp vụ khỏi UI và dễ dàng bảo trì.
- Lựa chọn công nghệ phụ thuộc vào việc form của bạn là "UI-driven" (CRUD đơn giản) hay "Business-logic driven" (quy trình quyết định phức tạp).
Khi nào mô hình biểu mẫu React truyền thống trở nên quá tải?
Đa số các nhà phát triển React đều chia sẻ một "mental model" chung: biểu mẫu luôn là các component. Chúng ta thường sử dụng một stack tiêu chuẩn:
- React Hook Form: Quản lý state cục bộ, giảm thiểu re-render.
- Zod: Xác thực dữ liệu (validation) và đảm bảo kiểu dữ liệu an toàn.
- React Query: Xử lý submission, caching và đồng bộ server.
Tuy nhiên, khi biểu mẫu bắt đầu tích lũy các quy tắc hiển thị (visibility rules) phụ thuộc vào câu trả lời trước đó hoặc các giá trị dẫn xuất (derived values) phức tạp, cấu trúc component sẽ trở nên khó kiểm soát. Lúc này, biểu mẫu không còn là UI đơn thuần mà đã trở thành một "quy trình quyết định".
Bảng so sánh các mô hình xây dựng biểu mẫu
| Đặc điểm | Component-Driven (RHF + Zod) | Schema-Driven (SurveyJS) |
|---|---|---|
| Định nghĩa | Code (JSX/TSX) | Dữ liệu (JSON Schema) |
| Logic nghiệp vụ | Nằm trong Component/Hook | Nằm trong Schema |
| Khả năng bảo trì | Cần can thiệp code khi thay đổi | Thay đổi cấu hình mà không cần deploy |
| Phù hợp cho | CRUD, Login, Settings | Quy trình phức tạp, khảo sát, workflow |
Phần 1: Tiếp cận theo hướng Component-Driven
Với React Hook Form và Zod, chúng ta định nghĩa schema bằng code. Khi gặp các quy tắc phụ thuộc chéo, chúng ta phải sử dụng superRefine.
// Ví dụ về logic phụ thuộc chéo trong Zod
export const formSchema = z.object({
hasAccount: z.enum(["Yes", "No"]),
username: z.string().optional(),
// ...
}).superRefine((data, ctx) => {
if (data.hasAccount === "Yes" && !data.username) {
ctx.addIssue({ code: "custom", path: ["username"], message: "Required" });
}
});
Nhược điểm của cách này là logic hiển thị bị phân tán trong JSX, khiến việc kiểm tra luồng nghiệp vụ trở nên khó khăn nếu không thực thi code trong đầu.
Phần 2: Tiếp cận theo hướng Schema-Driven (SurveyJS)
SurveyJS thay đổi tư duy bằng cách coi biểu mẫu là một JSON schema. Mọi thứ từ cấu trúc, validation, cho đến logic hiển thị (visibleIf) đều nằm trong dữ liệu.
Sơ đồ luồng xử lý dữ liệu
[JSON Schema] ➔ [SurveyJS Model Engine] ➔ [React UI Rendering]
▲ │ │
└───────────────────┴────────────────────────┘
(Tự động cập nhật khi dữ liệu thay đổi)
Việc sử dụng visibleIf: "{hasAccount} = 'Yes'" trong JSON giúp logic nghiệp vụ trở nên tường minh, tách biệt hoàn toàn khỏi tầng giao diện. Điều này cực kỳ hữu ích khi bạn cần xây dựng các ứng dụng yêu cầu khả năng tiếp cận cao, tương tự như các kỹ thuật tối ưu hóa Accessibility mà chúng ta từng thảo luận.
Lời khuyên lựa chọn
Để quyết định mô hình nào phù hợp, hãy tự hỏi: "Nếu xóa bỏ biểu mẫu này, tôi mất đi cái gì?".
- Chọn React Hook Form + Zod khi: Biểu mẫu mang tính chất CRUD, logic đơn giản, kỹ sư sở hữu toàn bộ quy trình.
- Chọn SurveyJS khi: Biểu mẫu là "quy trình nghiệp vụ", cần khả năng audit, versioning, hoặc cho phép phi kỹ sư (PM/Ops) điều chỉnh logic mà không cần can thiệp vào mã nguồn.
Việc hiểu rõ bản chất của biểu mẫu sẽ giúp bạn tối ưu hóa kiến trúc, giống như cách chúng ta tối ưu hóa các hiệu ứng CSS thông qua Scroll-Driven Animations để đạt hiệu suất tốt nhất.
Do you like this post?
Upvote to push this post higher on the community feed
