Back to articles

Understanding the React useEffect Hook Once and For All

The useEffect hook is arguably the most powerful, yet most misunderstood, hook in React. When I first transitioned from class components to functional components, I treated useEffect like componentDidMount and componentDidUpdate. This was a huge mistake.

Here is how I reframed my understanding of useEffect to avoid infinite loops and memory leaks.

It’s About Synchronization, Not Lifecycles

The official React documentation puts it best: useEffect is for synchronizing your React component with an external system.

External systems include:

  • Network requests (APIs)
  • Browser DOM (document.title, event listeners)
  • Third-party libraries (like initializing a chart or a slider)

If you are using useEffect just to update a piece of state based on another piece of state, you are probably using it wrong.

The Dependency Array is a Contract

The second argument to useEffect—the dependency array—is a contract you make with React. You are saying: “Only re-run this synchronization if one of these specific variables has changed since the last render.”

useEffect(() => {
  const handleScroll = () => console.log(window.scrollY);
  window.addEventListener('scroll', handleScroll);
  
  // The cleanup function is crucial to prevent memory leaks!
  return () => window.removeEventListener('scroll', handleScroll);
}, []); // Empty array means: only run on mount and unmount

A Real-World Portfolio Example

In this portfolio, when you search for a blog post, I use useEffect to synchronize the search query state with the fuse.js filtering logic. However, I added a setTimeout inside the effect to “debounce” the search.

By cleaning up the timeout in the return function, I ensure that the search only runs after the user stops typing for 300ms, saving processing power and keeping the UI buttery smooth.

Mastering useEffect is a milestone for any React developer. It changes how you think about your UI, not as a sequence of events, but as a state machine synchronizing with the outside world.