Race condition xảy ra khi effect chạy lại trước lúc request cũ trả về — hai response về không đúng thứ tự và bản cũ ghi đè bản mới. Không có gì báo lỗi; giao diện chỉ đơn giản hiện sai dữ liệu.
Hình dung ô tìm kiếm: người dùng gõ "re", rồi "rea", rồi "react". Ba request bay đi. Mạng không đảm bảo thứ tự trả về, nên nếu response của "re" về sau cùng, danh sách kết quả sẽ là của "re" trong khi ô nhập đang hiện "react".
Hai cách xử lý, cùng một ý: bỏ kết quả của lần chạy đã cũ.
// Cách 1 — AbortController: huỷ hẳn request cũ
useEffect(() => {
const ac = new AbortController()
fetch(`/api/search?q=${query}`, { signal: ac.signal })
.then((r) => r.json())
.then(setResults)
.catch((e) => { if (e.name !== 'AbortError') setError(e) })
return () => ac.abort()
}, [query])
// Cách 2 — cờ ignore: request vẫn chạy nhưng kết quả bị bỏ qua
useEffect(() => {
let ignore = false
search(query).then((r) => { if (!ignore) setResults(r) })
return () => { ignore = true }
}, [query])Cách 1 tiết kiệm băng thông vì huỷ luôn request. Cách 2 đơn giản hơn và dùng được với API không nhận signal.
Lưu ý: Strict Mode ở môi trường phát triển chạy effect hai lần chính là để lộ ra lỗi này — nếu bạn thấy dữ liệu nhấp nháy hay sai sau lần chạy thứ hai, đó là dấu hiệu thiếu phần dọn dẹp chứ không phải lỗi của React. TanStack Query và SWR xử lý sẵn phần này.