Effect Dependencies
Effect Dependencies
The second argument to useEffect is the dependency array. It tells React when it should skip re-running the Effect.
useEffect(() => {
// Effect logic...
}, [dependency1, dependency2]);
React compares the current values in the dependency array with the values from the previous render using Object.is. If all values are identical, React skips the Effect. If even one value has changed, React runs the cleanup function (if any) and then runs the new Effect.
The Golden Rule of Dependencies
If you use a reactive value inside your Effect, you MUST include it in the dependency array.
Reactive values include:
- Props
- State variables
- Variables and functions declared directly inside the component body
If you fail to include a reactive value in the dependency array, your Effect will use a “stale closure” (an old version of the variable from a previous render), leading to bugs that are very difficult to track down.
The ESLint Linter
React provides an ESLint rule called exhaustive-deps. You should never ignore its warnings. If the linter complains about a missing dependency, the solution is rarely to suppress the warning.
Instead, you should either:
- Add the variable to the dependency array.
- Move the variable declaration inside the
useEffect(if it’s not used anywhere else). - Move the variable declaration outside the component entirely (if it doesn’t depend on props/state).
- Wrap the variable/function in
useMemooruseCallbackif its reference changes too often.
Objects and Arrays as Dependencies
Because React uses Object.is for comparison, it checks for referential equality.
function Profile({ user }) {
// 'options' is created brand new on EVERY render
const options = { id: user.id, active: true };
useEffect(() => {
fetchData(options);
}, [options]); // ❌ Infinite loop or constant re-fetching!
}
In the example above, the options object is recreated on every render. Because {} is never equal to {}, the dependency array check fails every time, and the Effect runs continuously.
Solutions for Object/Array dependencies:
- Read primitive values directly in the array:
[user.id] - Move the object creation inside the Effect.
- Memoize the object using
useMemo.
Interview Questions
Q: Why is it dangerous to omit variables used inside useEffect from the dependency array?
A: If you omit a dependency, the effect will “close over” the stale value from the previous render cycle. This leads to insidious bugs where your effect acts on completely outdated state or props, rather than the current ones.