How to Update useEffect Hook in React

The useEffect hook is a fundamental tool in React for managing side effects in functional components. This article provides a straightforward guide on how to update and trigger the useEffect hook using the dependency array, explaining how to control when your effect runs, how to handle state and prop updates, and how to properly clean up effects to prevent memory leaks.

Understanding the Dependency Array

The key to updating or re-triggering a useEffect hook is its second argument, known as the dependency array. React uses this array to determine whether it should skip or execute the effect after a render.

There are three ways to configure the dependency array to control when the hook updates:

1. No Dependency Array (Updates on Every Render)

If you do not provide a dependency array, the effect will run after the initial render and after every subsequent update (render) of the component.

useEffect(() => {
  console.log('This runs after every single render');
});

2. Empty Dependency Array (Runs Once)

If you pass an empty array [], the effect runs exactly once after the initial render (component mount). It will not run on subsequent updates, even if state or props change.

useEffect(() => {
  console.log('This runs only once when the component mounts');
}, []);

3. Array with Dependencies (Updates on Specific Changes)

To update the effect only when specific values change, pass those values inside the dependency array. React will compare the current value of the dependency with its value from the previous render. If any value in the array has changed, the effect runs again.

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

useEffect(() => {
  console.log(`The count is now ${count}`);
}, [count]); // This effect updates only when 'count' changes

How to Handle Object and Array Dependencies

React uses shallow comparison (strict equality ===) to detect changes in the dependency array. Because objects and arrays in JavaScript are compared by reference rather than value, passing them directly into the dependency array can cause the effect to run on every render if the reference changes.

To safely update useEffect with objects or arrays, use one of the following approaches:

// Deconstructing properties for the dependency array
const user = { name: "Alice", age: 30 };

useEffect(() => {
  console.log(`User age changed to: ${user.age}`);
}, [user.age]); // Only triggers when 'age' changes, not the whole object reference

Cleaning Up Before the Next Update

When a dependency changes and triggers an update, React runs a cleanup function (if provided) from the previous execution before running the effect again. This prevents memory leaks and stale data.

To clean up an effect, return a function from inside your useEffect callback:

useEffect(() => {
  const handleResize = () => console.log(window.innerWidth);
  window.addEventListener('resize', handleResize);

  // Cleanup function runs before the next update or unmount
  return () => {
    window.removeEventListener('resize', handleResize);
  };
}, []);