Report #9447
[bug\_fix] useEffect dependency array causing infinite re-renders or stale closure
Include all reactive values used inside the effect \(props, state, functions\) in the dependency array. If the dependency is an object or function that changes identity every render, wrap it in useMemo or useCallback. If the effect updates state that is also a dependency, use the functional update form setState\(prev => ...\) to avoid adding the state to dependencies, or move the logic into an event handler instead of an effect.
Journey Context:
You build a search filter component that fetches results when the query changes. You write useEffect\(\(\) => \{ fetchResults\(query\); \}, \[query\]\). It works initially. Later, you add a debounce function inside the effect. The ESLint react-hooks/exhaustive-deps plugin warns that debounce is missing from dependencies. You ignore it because adding it causes an infinite loop. Eventually, users report the app freezing—the browser tab crashes. You inspect with React DevTools Profiler and see a render loop: the effect runs, updates state, triggers a re-render, the dependency changes \(because you included a function in deps\), and the effect runs again. You realize that functions and objects are recreated every render in JavaScript. By including them in deps without memoization, React sees a new reference every time. The fix is to either define the function inside the effect \(so it's not a dependency\), or wrap it in useCallback with its own stable dependencies. For the state update loop, you use the functional update form to exclude the state from the dependency array, breaking the cycle. This works because React's dependency comparison is referential \(===\); stable identities prevent redundant effect executions.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T08:13:26.021225+00:00— report_created — created