Blog / Others/ Understanding React useEffect Dependency Array: The Complete Guide

Understanding React useEffect Dependency Array: The Complete Guide

Understanding React useEffect Dependency Array: The Complete Guide

Understanding React useEffect Dependency Array: The Complete Guide

If you have been working with React for any amount of time, you have probably encountered the infamous useEffect hook. And if you have used useEffect, you have definitely struggled with the dependency array. Why does my effect run infinitely? Why does not it run when I expect? Why does ESLint keep warning me?

Let us demystify this once and for all.

What is the Dependency Array?

The dependency array is the second argument you pass to useEffect. It is an array of values that your effect depends on. When any of these values change, React will re-run your effect.

useEffect(() => {
  // Your effect logic here
}, [dependency1, dependency2]); // This is the dependency array

The Three Rules of Dependency Array

1. Empty Array = Run Once

When you pass an empty array, your effect runs only once after the initial render (similar to componentDidMount):

useEffect(() => {
  console.log("Component mounted!");
  // This runs only once
}, []);

2. No Array = Run Every Render

When you omit the array entirely, your effect runs after every render:

useEffect(() => {
  console.log("Runs after every render!");
  // This runs after EVERY render - danger!
});

3. With Dependencies = Run When They Change

Your effect runs when any dependency value changes:

const [count, setCount] = useState(0);

useEffect(() => {
  console.log("Count changed:", count);
}, [count]); // Runs when count changes

Common Mistakes and How to Fix Them

Mistake #1: The Infinite Loop

This happens when your effect updates a state that is also in the dependency array:

// WRONG - causes infinite loop
const [data, setData] = useState(null);

useEffect(() => {
  fetchData().then(result => setData(result));
}, [data]); // Updates data, which triggers effect again!

Fix: Remove the state variable from dependencies if you are setting it in the effect:

// CORRECT
useEffect(() => {
  fetchData().then(result => setData(result));
}, []); // Empty array - run once on mount

Mistake #2: Forgetting Dependencies

ESLint will warn you about this. Always include all values that your effect uses:

// WRONG - ESLint will complain
useEffect(() => {
  console.log("User:", user.name);
}, []); // Missing user dependency!
// CORRECT
useEffect(() => {
  console.log("User:", user.name);
}, [user]);

Mistake #3: Object/Array Dependencies

Objects and arrays in dependencies can cause unnecessary re-renders because they are compared by reference:

// PROBLEMATIC - new object every render
useEffect(() => {
  doSomething(config);
}, [config]); // config is { theme: "dark", fontSize: 16 }

const config = { theme: "dark", fontSize: 16 }; // Created new each render!

Fix: Use primitive values or use useMemo:

// BETTER
useEffect(() => {
  doSomething(config.theme, config.fontSize);
}, [config.theme, config.fontSize]);

The Cleanup Function

Return a function from your effect to clean up (similar to componentWillUnmount):

useEffect(() => {
  const subscription = api.subscribe(data => {
    setData(data);
  });

  // Cleanup function
  return () => {
    subscription.unsubscribe();
  };
}, []);

Quick Reference Table

Dependency Array When It Runs
No second argument After every render
Empty array [] Only on mount (once)
[value1, value2] When value1 or value2 changes

Pro Tips

  • Use ESLint exhaustive-deps rule - It will catch missing dependencies
  • Prefer primitive values in dependencies when possible
  • Use useCallback to stabilize function references
  • Use useMemo to stabilize object/array references
  • Move logic inside effect if it only affects the component

Conclusion

The dependency array is one of the most powerful yet confusing parts of useEffect. Remember: it is React way of knowing when to re-run your side effects. Treat it with respect, and your components will behave predictably.

Master this, and you will eliminate countless hours of debugging mysterious re-renders and infinite loops.

Post a Comment

Your email will not be published. Required fields are marked with *.