All posts
ReactSeptember 3, 20267 min read

React useEffect Hook Explained for Beginners

What is the React useEffect hook? A beginner-friendly guide with simple code examples that explain side effects, the dependency array, and cleanup step by step.

F
Fepiq Team
Fepiq

If you searched for "react useEffect hook explained for beginners," here is the short answer: useEffect is a built-in React function that lets a component do something extra after it renders on the screen — like fetching data, starting a timer, or changing the page title. In this guide you will learn exactly what that means, with small code examples you can copy and run yourself.

This post assumes you already know what a React component is and how the useState hook works. If those are new to you, read our guide on the useState hook first, then come back here.

What Does "Side Effect" Mean?

In plain English, a side effect is anything a component does that reaches outside of just showing information on the screen. Normally, a React component takes some data (props and state) and returns some HTML-like markup (JSX) to display. That is its main job.

But sometimes a component needs to do more than that. For example, it might need to:

  • Fetch data from a server (an API)
  • Change the browser tab's title
  • Start or stop a timer
  • Listen for a keyboard or window event
  • Save something to the browser's local storage

These extra tasks are called side effects, because they happen "on the side" of the normal render process. The useEffect hook is React's official way to run this kind of code safely.

The Basic Syntax of useEffect

Here is the smallest possible example: a component that changes the browser tab title every time it renders.

jsx
import { useEffect } from "react";

function PageTitle() {
  useEffect(() => {
    document.title = "Welcome to my page!";
  });

  return <h1>Hello there</h1>;
}

Line by line: first, we import useEffect from the "react" package, the same way you would import useState. Inside the component, we call useEffect(...) and pass it a function — this is called the effect function. Everything inside that function runs after React has finished putting the component on the screen. In this example, the effect function sets document.title, which is the browser's built-in way of changing the tab's title text.

The Dependency Array Explained

By default, the effect function runs after every single render. That is often not what you want, so useEffect accepts a second argument called the dependency array. It controls when the effect runs.

jsx
// Runs after every render
useEffect(() => {
  console.log("I run every time");
});

// Runs only once, after the first render
useEffect(() => {
  console.log("I run only once");
}, []);

// Runs after the first render, and again whenever "count" changes
useEffect(() => {
  console.log("count changed to", count);
}, [count]);

The first useEffect has no second argument at all, so it runs after every render — this is rarely what you want, because it can slow things down. The second one passes an empty array []. An empty array means "there is nothing to watch for changes," so React only runs the effect once, right after the component first appears. The third one passes [count]. This tells React to watch the count variable, and only re-run the effect when count is different from its previous value.

Dependency arrayWhen the effect runs
No array at allAfter every render (usually avoid this)
Empty array []Once, right after the first render
[value1, value2]After the first render, and again whenever value1 or value2 changes

Example: Fetching Data When a Component Loads

The most common beginner use case is loading data from an API as soon as a component appears on screen. Here is a small example that fetches a list of users.

jsx
import { useState, useEffect } from "react";

function UserList() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/users")
      .then((response) => response.json())
      .then((data) => setUsers(data));
  }, []);

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

Here, useState([]) creates a piece of state called users that starts as an empty array, plus a function setUsers to update it. The useEffect call fetches data from a web address (URL) and, once the response arrives, converts it to JSON and passes it to setUsers. We use an empty dependency array [] because we only want this fetch to happen once, when the component first loads — not every time it re-renders. Finally, the JSX uses users.map(...) to turn each user object into a list item, and key={user.id} helps React keep track of each item efficiently.

Example: Cleaning Up an Effect (a Timer)

Some effects need to be "undone" when the component disappears from the screen, or before the effect runs again. A classic example is a timer: if you start one, you should also stop it. You do this by returning a cleanup function from inside useEffect.

jsx
import { useState, useEffect } from "react";

function Clock() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const timerId = setInterval(() => {
      setSeconds((previous) => previous + 1);
    }, 1000);

    return () => {
      clearInterval(timerId);
    };
  }, []);

  return <p>Seconds elapsed: {seconds}</p>;
}

setInterval(..., 1000) runs its function every 1000 milliseconds (one second), and setSeconds((previous) => previous + 1) adds one to the counter each time. The important part is the return () => { clearInterval(timerId); } line at the end of the effect. This is the cleanup function. React automatically calls it when the component is removed from the page, so the timer stops instead of running forever in the background — a common bug beginners run into when they skip cleanup.

Common useEffect Mistakes Beginners Make

  • Forgetting the dependency array, which makes the effect run after every render and can slow the app down or cause loops
  • Putting a variable inside the effect but leaving it out of the dependency array, which can cause the effect to use old, outdated values
  • Starting a timer, event listener, or subscription without returning a cleanup function to stop it
  • Calling useEffect inside an if statement or a loop — hooks must always be called in the same order, at the top level of the component
  • Using useEffect just to calculate a value from existing state or props, when a plain variable would work fine

useState vs useEffect: What Is the Difference?

useStateuseEffect
PurposeStores and updates data inside a componentRuns code as a reaction to rendering or data changing
ReturnsA value and a function to update itNothing directly (optionally a cleanup function)
Typical useForm input, counters, togglesFetching data, timers, subscriptions, changing the page title

Frequently asked questions

What is useEffect in React, in one sentence?+

useEffect is a React hook that lets a component run extra code — like fetching data or starting a timer — after it renders, instead of during the render itself.

When should I use an empty dependency array []?+

Use an empty array when you want the effect to run only once, right after the component first appears on screen, such as loading initial data from an API.

Why does my useEffect run twice in development?+

In React's Strict Mode, which is on by default in new projects during development, React intentionally runs effects twice to help you catch missing cleanup functions. This does not happen in production.

Do I always need a cleanup function?+

No. You only need one when your effect starts something ongoing, like a timer, an event listener, or a subscription, that should be stopped when the component goes away.

Can I use useEffect for form validation?+

You can, but for simple validation it is usually simpler and faster to check the value directly while handling the input's onChange event, and save useEffect for things that involve the world outside the component, like an API call.

New to hooks? Start with the basics of managing data in a component before diving into side effects.

Read the useState guide

useEffect can feel confusing at first, but it comes down to one idea: run this code after rendering, and optionally clean it up later. Start with the empty array [] pattern for data fetching, add specific values to the array only when you need the effect to react to changes, and always clean up timers and subscriptions. Practice with the examples above in a small project, and it will click quickly.

Building a React app and want a second pair of eyes on your architecture?

Talk to our team

Get new posts in your inbox

Occasional, no-fluff notes on shipping modern software — startups, automation, Laravel, Shopify and more. No spam, unsubscribe anytime.

Keep reading

Related posts

All posts

Let's build something

Ready to ship your next product with Fepiq?

Book a free discovery call. We'll listen, ask sharp questions, and send you a proposal within 3 business days.