Dependency array là danh sách những giá trị mà effect phụ thuộc vào; React chạy lại effect khi một trong số đó đổi. Nó không phải bộ hẹn giờ hay công tắc — nó là lời khai báo "effect này đọc những thứ này".
Ba dạng, khác nhau hoàn toàn:
useEffect(() => { ... }) // không có array → chạy sau MỌI lần render
useEffect(() => { ... }, []) // array rỗng → chạy sau mỗi lần mount
useEffect(() => { ... }, [a, b]) // chạy lại khi a hoặc b đổiReact so sánh từng phần tử bằng Object.is, tức so tham chiếu với object và hàm.
Đây là nguồn của hai lỗi trái ngược nhau:
// Effect chạy mỗi render: options là object mới mỗi lần
const options = { limit: 10 }
useEffect(() => { fetchData(options) }, [options])
// Chữa: đưa giá trị nguyên thuỷ vào deps
useEffect(() => { fetchData({ limit }) }, [limit])Chiều ngược lại là thiếu dependency: effect đọc một biến nhưng biến đó không có trong array, nên effect giữ mãi giá trị của lần render nó được tạo ra. Bug hiện ra dưới dạng "dữ liệu cũ" mà không có lỗi nào ném ra. Quy tắc ESLint react-hooks/exhaustive-deps bắt đúng lớp lỗi này — nên bật nó, và khi muốn bỏ qua thì phải viết rõ lý do chứ đừng tắt cảnh báo cho xong.
Lưu ý: Array rỗng [] không có nghĩa "chạy một lần" mà là "effect không phụ thuộc gì". Nếu bên trong effect có đọc props hay state thì [] là khai báo sai — và trong React 18 Strict Mode ở môi trường phát triển, effect còn được chạy hai lần cố ý để lộ ra những effect thiếu cleanup.