Mastering Side Effects: The `useEffect` Hook

Connect your components to the outside world for data fetching, subscriptions, timers, and much more.

Lesson ProgressStep 1 of 10
⚛️ ↔️ 🌐
0 EXP

Welcome! Our components are great for UI, but how do they interact with the outside world, like an API?

/* How to connect to the outside world? */

What is a Side Effect?

A "side effect" (or "effect") is any operation that affects something outside of the component's render logic. React's render process should be "pure"—given the same props and state, it should always return the same UI.

Anytime you need to "reach outside" your component, it's a side effect. Common examples include:

  • Fetching data from an API (like `fetch` or `axios`).
  • Setting up a subscription (like a WebSocket or `setInterval`).
  • Manually changing the DOM (like `document.title` or using a third-party library).

The `useEffect` hook provides a safe place for these effects to live, so they don't block the render or run at the wrong time.

System Check

Which of these is NOT a side effect?

Advanced Holo-Simulations

0 EXP

Log in to unlock these advanced training modules and test your skills.


Achievements

🏆
Effect Master

Write a complete `useEffect` hook for data fetching.

🏗️
Dependency Whiz

Correctly identify how the dependency array works.

✍️
Cleanup Expert

Prove your mastery of effect cleanup functions.

Mission: Fetch the Data

Complete the `DataFetcher` component. Use `useEffect` to fetch data from `https://api.example.com/data` **only once** when the component mounts and store the result in the `data` state.

A.D.A. Feedback:

> System integrity looks stable. Code is valid.

Challenge: Match the Behavior

Drag the `useEffect` snippets on the right to match the correct behavior description on the left.

Runs once on mount
Runs when 'id' changes
Runs on every render
useEffect(() => { ... }, [id])
useEffect(() => { ... }, [])
useEffect(() => { ... })

Challenge: Complete the Cleanup

Fill in the missing parts to create a `useEffect` hook that sets up a timer and correctly cleans it up.

(() => {const timerId = setInterval(() => {console.log('Tick');}, 1000);
// Cleanup function () => {(timerId);};}, []);

Consult A.D.A.

Community Holo-Net

Mastering `useEffect`: A Deep Dive

The `useEffect` hook is arguably one of the most important, and often misunderstood, hooks in React. It's the bridge between React's declarative UI and the imperative world of side effects. A **side effect** is any operation that affects something outside of the component's render, such as fetching data, setting up a timer, or manually manipulating the DOM.

The Anatomy of `useEffect`

At its core, `useEffect` accepts two arguments: a function (the "effect") and an optional dependency array.

useEffect(() => {
  // This is the "effect" function.
  // It runs *after* the component renders.
  
  return () => {
    // This is the optional "cleanup" function.
  };
}, [/* dependency array */]);

The magic of `useEffect` is all in the **dependency array**. It controls *when* the effect function is executed.

The 3 Flavors of the Dependency Array

Understanding the three ways to use the dependency array is the key to mastering `useEffect`.

  • 1. No Dependency Array (Runs on Every Render)

    If you omit the array, the effect will run after **every single render** (both the initial render and all re-renders). This is rarely what you want and often causes performance issues or infinite loops.

    useEffect(() => {
      console.log('Component re-rendered');
    }); // ⚠️ Runs *all* the time
  • 2. Empty Dependency Array `[]` (Runs Only Once)

    This tells React that your effect has no dependencies on props or state. The effect will run **only once**, right after the initial render (when the component "mounts"). This is the perfect pattern for "run-once" setup logic, like fetching initial data or setting up a static event listener.

    useEffect(() => {
      fetch('/api/user')
        .then(res => res.json())
        .then(setData);
    }, []); // ✅ Runs only once
  • 3. Populated Dependency Array `[prop, state]` (Runs When Deps Change)

    This is the most powerful pattern. The effect will run after the initial render, and then it will run **again** any time one of the values in the array changes. React performs a shallow comparison of each item. This is how you "synchronize" your component with a prop or state.

    useEffect(() => {
      fetch(`/api/posts/${postId}`)
        .then(res => res.json())
        .then(setPost);
    }, [postId]); // ✅ Re-runs when postId changes

The All-Important Cleanup Function

What if your effect sets up something that needs to be "undone"? For example, a `setInterval` timer, a WebSocket subscription, or a manual event listener. If you don't clean these up, you'll create **memory leaks**.

To clean up, you simply **return a function** from your effect. React will execute this cleanup function in two scenarios:

  1. When the component is removed from the screen (unmounts).
  2. Before the effect runs *again* (due to a dependency change).

✔️ Cleanup Practice: `setInterval`

useEffect(() => {
  // Start the timer
  const intervalId = setInterval(() => {
    console.log('Timer tick!');
  }, 1000);

  // Return the cleanup function
  return () => {
    console.log('Cleaning up timer');
    clearInterval(intervalId);
  };
}, []);

This ensures the timer is destroyed when the component unmounts, preventing it from running forever.

Common Patterns & Pitfalls

  • `async/await`: You cannot make the effect function itself `async`. Instead, define an `async` function *inside* the effect and call it.
  • Stale Closures: Be careful when an effect's function references state or props that change. If you don't include those values in the dependency array, the function will "remember" the old values. The `eslint-plugin-react-hooks` (with its `exhaustive-deps` rule) is your best friend here.
Key Takeaway: Think of `useEffect` not as a "lifecycle" hook, but as a "synchronization" hook. You are telling React: "Synchronize this piece of state with the outside world, and re-synchronize it *only* when these dependencies change."

`useEffect` & Side Effects Glossary

Side Effect
Any operation in a component that is not a "pure" calculation based on props and state. This includes data fetching (e.g., `fetch`), DOM manipulation (`document.title`), timers (`setInterval`), or subscriptions.
`useEffect`
A React Hook that lets you "synchronize" your component with an external system. It runs a function (the "effect") after the component has rendered.
Dependency Array
The optional second argument for `useEffect`. This array tells React which values (props or state) the effect "depends on." The effect will only re-run if one of these values changes.
Cleanup Function
An optional function that you can `return` from your effect. React runs this function to "clean up" the effect's work (e.g., cancel timers, remove listeners) before the component unmounts or before the effect runs again.
Mounting
The first time a component is rendered and added to the DOM. An effect with an empty `[]` dependency array runs only after mounting.
Unmounting
When a component is removed from the DOM. This is the last chance for a component to run any cleanup logic.
Stale Closure
A common bug where an effect "remembers" an old value of a prop or state because that value was not included in the dependency array. The ESLint `exhaustive-deps` rule helps prevent this.
Race Condition
A bug in data fetching where multiple requests are made, but an older, slower request finishes *after* a newer one, displaying stale data. Cleanup functions are used to ignore responses from old requests.

About the Author

Author's Avatar

TodoTutorial Team

Passionate developers and educators making programming accessible to everyone.

This article was written and reviewed by our team of senior React developers, who use these hooks daily to build robust and scalable web applications.

Verification and Updates

Last reviewed: November 2025.

We strive to keep our content accurate and up-to-date. This tutorial is based on the latest React v18+ specifications and is periodically reviewed to reflect industry best practices.

External Resources

Found an error or have a suggestion? Contact us!