Bài viết này đi qua những khó khăn phổ biến mà các nhà phát triển phải đối mặt khi xử lý biểu mẫu — và cách React 19 cuối cùng đã giới thiệu một số công cụ được mong đợi từ lâu giúp việc xử lý biểu mẫu trở nên sạch hơn, mang tính khai báo hơn và ít lỗi hơn nhiều.
Trong sáu năm qua trong phát triển frontend — từ việc xây dựng các hệ thống biểu mẫu phức tạp đến tích hợp các công cụ AI tại SDG — tôi đã viết, gỡ lỗi và tái cấu trúc nhiều mã biểu mẫu hơn tôi muốn thừa nhận.
Và nếu bạn đã từng xây dựng hoặc bảo trì biểu mẫu trong React, có lẽ bạn cũng có cảm giác tương tự. Chúng có vẻ đơn giản một cách lừa dối... cho đến khi không còn đơn giản nữa.
Trong bài viết này, tôi sẽ hướng dẫn bạn qua những khó khăn phổ biến mà các nhà phát triển phải đối mặt khi xử lý biểu mẫu — và cách React 19 cuối cùng đã giới thiệu một số công cụ được mong đợi từ lâu giúp việc xử lý biểu mẫu trở nên sạch hơn, mang tính khai báo hơn và ít lỗi hơn nhiều. ✨
🔍 Hãy bắt đầu với những điểm đau mà mọi nhà phát triển React đã từng gặp phải ít nhất một lần.
Quản lý trạng thái biểu mẫu trong React thường bắt đầu như thế này:
const [name, setName] = useState(''); const [surname, setSurname] = useState(''); const [error, setError] = useState(null); function handleSubmit(event) { event.preventDefault(); }
✅ Nó đơn giản — và hoàn toàn phù hợp cho các biểu mẫu nhỏ.
Nhưng ngay khi bạn mở rộng quy mô, bạn sẽ bị ngập trong các hook trạng thái lặp đi lặp lại, các lần đặt lại thủ công và vô số lệnh gọi event.preventDefault().
Mỗi lần nhấn phím kích hoạt một lần render lại, và việc quản lý lỗi hoặc trạng thái đang chờ xử lý đòi hỏi thêm nhiều biến trạng thái. Nó hoạt động, nhưng còn xa mới được gọi là thanh lịch.
Khi biểu mẫu của bạn không chỉ là một component mà là một hệ thống phân cấp của các component lồng nhau, bạn sẽ phải truyền props qua mọi cấp độ:
<Form> <Field error={error} value={name} onChange={setName}> <Input /> </Field> </Form>
Trạng thái, lỗi, cờ đang tải — tất cả được khoan qua nhiều lớp. 📉 Điều này không chỉ làm phình mã mà còn khiến việc bảo trì và tái cấu trúc trở nên đau đớn. 😓
Bạn đã bao giờ thử triển khai cập nhật lạc quan theo cách thủ công chưa?
Đó là khi bạn hiển thị thay đổi "thành công" trong giao diện người dùng ngay sau hành động của người dùng — trước khi máy chủ thực sự xác nhận nó.
Nghe có vẻ dễ dàng nhưng việc quản lý logic hoàn tác khi yêu cầu thất bại có thể là một cơn đau đầu thực sự. 🤕
Bạn lưu trữ trạng thái lạc quan tạm thời ở đâu? Làm thế nào để hợp nhất và sau đó hoàn tác nó? 🔄
React 19 giới thiệu một giải pháp sạch hơn nhiều cho vấn đề này.
Một trong những bổ sung thú vị nhất trong React 19 là hook ==*useActionState *==.
Nó đơn giản hóa logic biểu mẫu bằng cách kết hợp gửi biểu mẫu bất đồng bộ, quản lý trạng thái và chỉ báo đang tải — tất cả trong một nơi. 🎯
const [state, actionFunction, isPending] = useActionState(fn, initialState);
Đây là những gì đang xảy ra:
==fn== — hàm bất đồng bộ của bạn xử lý việc gửi biểu mẫu
==initialState== — giá trị ban đầu của trạng thái biểu mẫu
==isPending== — cờ tích hợp cho biết liệu việc gửi có đang diễn ra hay không
\
Hàm bất đồng bộ được truyền vào ==useActionState== tự động nhận hai đối số:
const action = async (previousState, formData) => { const message = formData.get('message'); try { await sendMessage(message); return { success: true, error: null }; } catch (error) { return { success: false, error }; } };
Sau đó bạn kết nối nó vào biểu mẫu của bạn như thế này:
const [state, actionFunction, isPending] = useActionState(action, { success: false, error: null, }); return <form action={actionFunction}> ... </form>;
Bây giờ, khi biểu mẫu được gửi đi, React tự động:
Không cần thêm ==useState, preventDefault,== thủ công hoặc logic đặt lại — React lo tất cả những điều đó. ⚙️
Nếu bạn quyết định kích hoạt hành động biểu mẫu theo cách thủ công (ví dụ: bên ngoài thuộc tính action của biểu mẫu), hãy bọc nó bằng ==startTransition==:
const handleSubmit = async (formData) => { await doSomething(); startTransition(() => { actionFunction(formData); }); };
Nếu không, React sẽ cảnh báo bạn rằng một cập nhật bất đồng bộ đã xảy ra bên ngoài quá trình chuyển tiếp, và ==isPending== sẽ không cập nhật đúng cách.
Logic biểu mẫu lại cảm thấy mang tính khai báo — chỉ cần mô tả hành động, không phải dây dẫn.
Một hook mới mạnh mẽ khác — ==useFormStatus== — giải quyết vấn đề prop drilling trong cây biểu mẫu.
import { useFormStatus } from 'react-dom'; const { pending, data, method, action } = useFormStatus();
Bạn có thể gọi hook này bên trong bất kỳ component con nào của biểu mẫu, và nó sẽ tự động kết nối với trạng thái của biểu mẫu cha.
function SubmitButton() { const { pending, data } = useFormStatus(); const message = data ? data.get('message') : ''; return ( <button type="submit" disabled={pending}> {pending ? `Sending ${message}...` : 'Send'} </button> ); } function MessageForm() { return ( <form action={submitMessage}> <SubmitButton /> </form> ); }
:::info Lưu ý rằng ==SubmitButton== có thể truy cập dữ liệu và trạng thái đang chờ xử lý của biểu mẫu — mà không cần truyền xuống bất kỳ props nào.
:::
🧩 Loại bỏ prop drilling trong cây biểu mẫu ⚡ Cho phép đưa ra quyết định theo ngữ cảnh bên trong các component con 💡 Giữ cho các component tách rời và sạch hơn
Cuối cùng, hãy nói về một trong những bổ sung y


