Understanding useEffect([]) in React – A Practical Guide

React useEffect with empty dependency array code example
AI-generated snippet: useEffect with empty array runs once on mount.

When you're working with React and functional components, one of the most useful hooks you'll come across is useEffect. It allows you to perform side effects in your components — like fetching data, setting up listeners, or interacting with the DOM.

One specific usage pattern of useEffect that confuses many developers early on is this:

useEffect(() => {
  // some code
}, []);

This is useEffect with an empty dependency array. In this article, we’ll look at what it means, why it’s used, and when to apply it in real-world situations.

What Is useEffect?

React's useEffect runs after the component renders. It’s similar to lifecycle methods like componentDidMount, componentDidUpdate, and componentWillUnmount in class components, but for function components.

The basic syntax looks like this:

useEffect(() => {
  // your side effect logic here
}, [/* dependencies */]);

The second argument — the dependency array — tells React when to run the effect.

Why Use an Empty Dependency Array?

When you pass an empty array ([]) as the second argument to useEffect, you're telling React:
“Execute this code just one time — after the component mounts and is placed into the DOM.”

This is useful for actions that should only happen once, such as:

  • Fetching initial data from an API
  • Setting up a subscription or event listener
  • Starting a timer or interval

Here’s an example:

useEffect(() => {
  fetchData();
}, []);

In this case, fetchData() will run only once — right after the component is mounted — and won’t run again on re-renders.

What Happens If You Skip the Dependency Array?

If you write useEffect like this:

useEffect(() => {
  fetchData();
});

Then fetchData() will run after every render.

This might lead to problems such as:

  • Repeated API calls
  • Infinite loops (especially if you update state inside useEffect)
  • Performance issues

So, always use the dependency array based on your needs. If you want the code to run only once, include the empty array.

Real-World Example

Let’s say you're building a to-do app and want to load the initial list from the server when the component mounts:

import { useEffect, useState } from 'react';

function TodoList() {
  const [todos, setTodos] = useState([]);

  useEffect(() => {
    async function fetchTodos() {
      const res = await fetch('/api/todos');
      const data = await res.json();
      setTodos(data);
    }

    fetchTodos();
  }, []);

  return (
    <ul>
      {todos.map(todo => <li key={todo.id}>{todo.title}</li>)}
    </ul>
  );
}

Here, the API is called only once — when the component loads — and the data is saved to state.

Cleaning Up Inside useEffect([])

Sometimes, you need to clean up something when the component is removed — for example, removing an event listener. You can return a function inside useEffect for this purpose.

useEffect(() => {
  const handleScroll = () => {
    console.log('User scrolled');
  };

  window.addEventListener('scroll', handleScroll);

  return () => {
    window.removeEventListener('scroll', handleScroll);
  };
}, []);

This cleanup function will run when the component unmounts.

Summary

Here’s what to remember about useEffect([]):

  • Use it when you want your side effect to run only once, just after the first render.
  • It’s similar to componentDidMount() in class components.
  • Helpful for API calls, event listeners, or timers that only need to run once.
  • Don’t forget the array — otherwise, your effect will run on every render.
  • Be cautious when updating state inside useEffect, especially if the dependencies are not handled properly.

Conclusion

Understanding how useEffect([]) works is a key part of working with React. It’s a simple concept, but very powerful in real-world applications. Once you get comfortable with this pattern, you'll be able to handle initial data loading, event setup, and many other side effects in a clean and predictable way.

If you're just starting with React hooks, mastering this one pattern will give you a strong foundation.

Comments