Effect Dependencies

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 2 min

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:

  1. Add the variable to the dependency array.
  2. Move the variable declaration inside the useEffect (if it’s not used anywhere else).
  3. Move the variable declaration outside the component entirely (if it doesn’t depend on props/state).
  4. Wrap the variable/function in useMemo or useCallback if 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:

  1. Read primitive values directly in the array: [user.id]
  2. Move the object creation inside the Effect.
  3. 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.