Vòng lặp vô hạn xảy ra khi effect cập nhật chính thứ mà nó phụ thuộc vào. Set state → render lại → dependency đổi → effect chạy → set state.
Chuỗi này không tự dừng.
// Lặp vô hạn: effect ghi vào count, mà count lại nằm trong deps
useEffect(() => {
setCount(count + 1)
}, [count])Ba nguyên nhân phổ biến và cách chữa tương ứng:
1. Effect ghi vào state nằm trong deps. Nếu giá trị mới chỉ phụ thuộc giá trị cũ, dùng updater và bỏ state khỏi deps:
useEffect(() => {
setCount((c) => c + 1)
}, [])**2.
Dependency là object hoặc hàm dựng lại mỗi render.** React so bằng tham chiếu nên lần nào cũng thấy "đổi":
// filters là object mới mỗi render → effect chạy lại sau MỌI lần render.
// Thành vòng lặp khi load() cũng set state.
useEffect(() => { load(filters) }, [filters])
// Chữa: bọc bằng useMemo, hoặc đưa giá trị nguyên thuỷ vào deps
const filters = useMemo(() => ({ status, page }), [status, page])3. Fetch rồi setState mà response luôn tạo object mới. Kiểm tra xem có thật sự cần state không, hoặc so sánh trước khi ghi.
Trước khi đi tìm cách chữa, hãy hỏi câu quan trọng hơn: effect này có cần tồn tại không? Rất nhiều vòng lặp vô hạn sinh ra từ effect chỉ để tính một giá trị từ state khác — thứ đó nên tính thẳng khi render, không cần effect và cũng không cần state:
const fullName = `${first} ${last}` // không cần effectLưu ý: useRef giữ giá trị qua các lần render mà không kích hoạt re-render, nên dùng được để cắt vòng lặp.
Nhưng nếu giá trị đó có hiển thị lên UI thì ref không phải công cụ phù hợp — thay đổi ref không làm React vẽ lại.