Callback truyền cho setInterval được tạo ở lần render đầu tiên và giữ nguyên mãi.
Nó đóng gói (closure) biến count của render đó — luôn là 0 — nên mỗi giây đều tính 0 + 1.
jsx
useEffect(() => {
const id = setInterval(() => setCount(count + 1), 1000) // always 0 + 1
return () => clearInterval(id)
}, [])Ba hướng sửa, chọn theo nhu cầu:
1. Functional updater — hay dùng nhất khi chỉ cần giá trị mới dựa trên giá trị cũ. Effect không cần đọc count nữa nên deps [] là đúng:
jsx
setInterval(() => setCount((c) => c + 1), 1000)2. Khai báo dependency — [count] cũng chạy đúng, nhưng interval bị huỷ và tạo lại mỗi giây, làm lệch nhịp đếm.
3. Ref giữ callback mới nhất — khi callback cần đọc nhiều state/props chứ không chỉ cộng dồn: lưu callback vào ref, effect setup một lần và gọi ref.current().
jsx
const savedRef = useRef(callback)
useEffect(() => { savedRef.current = callback })
useEffect(() => {
const id = setInterval(() => savedRef.current(), delay)
return () => clearInterval(id)
}, [delay])