Tại Sao Phải update() Xong Mới Gọi resetModel()? Tiêu Chuẩn Hay Lỗi Tư Duy?
Trong quá trình làm việc với form cập nhật dữ liệu, chắc hẳn chúng ta đều đã viết (hoặc nhìn thấy) một đoạn code có flow xử lý tương tự như thế này:
async function handleUpdate() {
// 1. Gọi API để update dữ liệu xuống DB
await updateData(this.model);
// 2. Reset lại model (xóa form, hoặc load lại data mới)
this.resetModel();
}
Một câu hỏi rất hay thường được đặt ra bởi các new-bie hoặc trong các buổi phỏng vấn là: "Tại sao không gọi resetModel() trước hoặc ngay cùng lúc với update() cho nhanh, mà bắt buộc phải đợi update() chạy xong mới được gọi?"
Để trả lời câu hỏi này dưới góc nhìn của một kỹ sư hệ thống thay vì một thợ gõ code, chúng ta cần phân tích dựa trên 3 nguyên tắc cốt lõi sau:
1. Triết lý "Dữ liệu là Đạn" (Payload Integrity)
Hãy tưởng tượng this.model chính là băng đạn, còn hàm update() là hành động bóp cò. Khi bạn làm việc với các framework hiện đại, model thường được bind (trói buộc) trực tiếp với giao diện (form input). Nếu bạn gọi resetModel() trước khi dữ liệu thực sự được đóng gói và gửi đi (hoặc trong lúc HTTP Request đang bay trên mạng), bạn đang tự tay "tháo đạn" trước khi bắn.
Tùy thuộc vào cơ chế truyền tham chiếu (pass-by-reference) của ngôn ngữ, việc reset model quá sớm có thể dẫn đến việc Payload gửi xuống Backend bị rỗng (null hoặc default values). Kết quả là Backend nhận được một cục data trống trơn và ném ra lỗi 422 Unprocessable Entity hoặc tồi tệ hơn là ghi đè dữ liệu rỗng vào Database.
2. Nghệ Thuật Xử Lý Lỗi (Error Handling & Graceful Degradation)
Đây là lý do quan trọng nhất ảnh hưởng trực tiếp đến trải nghiệm người dùng (UX). Không có gì đảm bảo 100% rằng hàm update() của bạn sẽ luôn thành công. Giao dịch có thể thất bại vì muôn vàn lý do:
- Validation Backend trả về lỗi (Ví dụ: Email đã tồn tại).
- Database bị deadlock.
- Mất kết nối mạng (Timeout).
Nếu bạn gọi resetModel() TRƯỚC khi update() hoàn tất:
Form của người dùng sẽ bị xóa sạch bong. Vài giây sau, hệ thống báo lỗi "Cập nhật thất bại". Lúc này, người dùng nhìn vào một cái form trống rỗng, dữ liệu họ vừa hì hục nhập đã bốc hơi, và họ phải gõ lại từ đầu. Đây là thảm họa UX có thể khiến user đập máy.
Nếu bạn gọi resetModel() SAU khi update() thành công (await / then):
Nếu có lỗi xảy ra, tiến trình sẽ bị ngắt (nhảy vào block catch). Hàm resetModel() sẽ không được thực thi. Form vẫn giữ nguyên dữ liệu mà người dùng vừa nhập, kèm theo dòng thông báo lỗi màu đỏ. Người dùng chỉ việc sửa đúng field bị sai và bấm submit lại. Tuyệt vời!
3. Nguyên Tắc "Nguồn Chân lý Duy Nhất" (Single Source of Truth)
Sau khi một thao tác update thành công ở dưới Database, bản thân dữ liệu đó có thể đã bị thay đổi bởi các logic của Backend mà Frontend không hề biết. Ví dụ:
- Trigger tự động cập nhật trường
updated_at. - Cột
slugtự động được sinh ra từtitle. - Các trường tính toán (computed fields) được cập nhật lại.
Hàm resetModel() trong nhiều kiến trúc (đặc biệt là dạng Datatable hoặc Edit Modal) không chỉ đơn thuần là "xóa trắng form", mà nó mang ý nghĩa là "Đồng bộ lại trạng thái của UI với trạng thái mới nhất của Database".
Do đó, bạn bắt buộc phải đợi Backend xử lý xong xuôi (Commit Transaction), trả về tín hiệu 200 OK, thì lúc đó bạn mới được gọi resetModel() để re-fetch (tải lại) danh sách dữ liệu mới nhất. Làm ngược lại, giao diện của bạn sẽ hiển thị dữ liệu "cũ" hoặc dữ liệu "ảo" không khớp với DB.
4. Ngoại Lệ: Optimistic UI (Cập nhật lạc quan)
Có bao giờ chúng ta reset giao diện ngay lập tức mà không thèm chờ Backend phản hồi chưa? Có. Đó là kiến trúc Optimistic UI. (Thường thấy khi bấm Like Facebook hay thả tim trên Zalo).
Khi bấm Like, trái tim đỏ ngay lập tức, model thay đổi ngay, không cần chờ API. Tuy nhiên, đằng sau đó, developer không hề "gọi bừa", mà họ áp dụng một luồng cực kỳ tinh vi:
- Lưu lại bản sao (Snapshot) của model hiện tại.
- Update/Reset model trên UI ngay lập tức để user thấy phản hồi tức thì.
- Gọi
update()ngầm dưới background (gọi API). - Nếu API lỗi: Âm thầm dùng bản Snapshot ở bước 1 để rollback lại giao diện (thu hồi lượt Like) và báo lỗi nhỏ.
Kết Luận
Việc đặt resetModel() sau update() không phải là một thói quen viết code ngẫu nhiên. Nó là kết tinh của việc bảo vệ tính toàn vẹn dữ liệu (Data Integrity), quản lý luồng trạng thái (State Flow) và sự tôn trọng đối với công sức nhập liệu của người dùng.
Trong lập trình, đôi khi thứ tự của 2 dòng code lại là ranh giới giữa một hệ thống "chạy được" và một hệ thống "đạt chuẩn".
*** Anh em có từng bị dính bug nào liên quan đến việc đặt sai thứ tự cập nhật state này chưa? Cùng chia sẻ dưới phần bình luận nhé!
All rights reserved