gotchajavascriptreactMajorpending
Gotcha: React useEffect dependency array with objects
Viewed 0 times
useEffectdependencyreferenceequalityuseMemoinfinite-loop
Error Messages
Problem
useEffect runs on every render even though the object 'looks' the same, because objects are compared by reference, not by value.
Solution
Don't put object/array literals in dependency arrays:
// BAD - runs every render because {} !== {} by reference:
useEffect(() => {
fetchData(options);
}, [{ page: 1, limit: 10 }]); // New object every render!
// GOOD - use primitive values:
useEffect(() => {
fetchData({ page, limit });
}, [page, limit]);
// GOOD - memoize the object:
const options = useMemo(() => ({ page, limit }), [page, limit]);
useEffect(() => {
fetchData(options);
}, [options]);
// GOOD - JSON.stringify for complex deps (escape hatch):
const optionsKey = JSON.stringify(options);
useEffect(() => {
fetchData(JSON.parse(optionsKey));
}, [optionsKey]);
// For event handlers, use useCallback:
const handleClick = useCallback(() => {
doSomething(value);
}, [value]);
// BAD - runs every render because {} !== {} by reference:
useEffect(() => {
fetchData(options);
}, [{ page: 1, limit: 10 }]); // New object every render!
// GOOD - use primitive values:
useEffect(() => {
fetchData({ page, limit });
}, [page, limit]);
// GOOD - memoize the object:
const options = useMemo(() => ({ page, limit }), [page, limit]);
useEffect(() => {
fetchData(options);
}, [options]);
// GOOD - JSON.stringify for complex deps (escape hatch):
const optionsKey = JSON.stringify(options);
useEffect(() => {
fetchData(JSON.parse(optionsKey));
}, [optionsKey]);
// For event handlers, use useCallback:
const handleClick = useCallback(() => {
doSomething(value);
}, [value]);
Why
React uses Object.is() for dependency comparison, which checks reference equality. Two identical objects at different memory addresses are not equal.
Revisions (0)
No revisions yet.