React Course
React
/
Intermediate

Side Effects (useEffect)

Definition

The `useEffect` hook lets you perform side effects in functional components. A side effect is anything that reaches outside the React component (like fetching data, manually changing the DOM, or setting up subscriptions).

Explain Like I'm New

React's main job is just turning data into UI. But sometimes you need to do things outside of that process, like knocking on a server's door to ask for data, or setting up a ticking clock. `useEffect` is a designated safe space where you can do these 'outside' tasks after React has safely finished painting the screen.

Real World Example

When a 'Profile' component appears on screen, you need to fetch that user's data from a database. You put the fetch request inside `useEffect` so it runs exactly once when the profile loads.

Common Use Cases

  • Fetching data from an API on component mount
  • Setting up event listeners (like window resize)
  • Interacting with non-React libraries (like a D3 chart)

Dependency Array Behaviors

Array TypeExampleWhen does the effect run?
No Array`useEffect(() => {...})`Runs on the very first render AND after absolutely every single re-render of the component. (High risk of infinite loops).
Empty Array`useEffect(() => {...}, [])`Runs strictly ONCE when the component mounts to the screen. Never runs again.
Variables Included`useEffect(() => {...}, [id])`Runs on the first render, and then ONLY runs again if the value of `id` has mathematically changed.

Interactive Example

import React, { useState, useEffect } from 'react';

export default function Timer() {
  const [seconds, setSeconds] = useState(0);

  // This effect runs exactly ONCE on mount because of []
  useEffect(() => {
    console.log("Timer started!");
    
    // Set up a side effect (an interval)
    const intervalId = setInterval(() => {
      setSeconds(prev => prev + 1);
    }, 1000);

    // Cleanup Function
    // Runs when the component unmounts (is removed from screen)
    return () => {
      console.log("Component destroyed, clearing interval!");
      clearInterval(intervalId);
    };
  }, []); // Empty dependency array

  return (
    <div>
      <h2>Seconds elapsed: {seconds}</h2>
    </div>
  );
}

Interview Questions

basic

  • What is the dependency array in useEffect?
  • What happens if you leave the dependency array completely empty `[]`?

intermediate

  • What happens if you don't provide a dependency array at all?
  • What is the cleanup function in useEffect and when does it run?

advanced

  • Why shouldn't you make the useEffect callback itself an `async` function?
  • How do you deal with 'stale closures' inside useEffect?

trick

  • If you set state inside a useEffect that has no dependency array, what happens?

Flash Cards

Question

What happens if you don't provide a dependency array at all?

Click to reveal answer
Answer

The useEffect will run after EVERY single render of the component. This is rarely what you want and often leads to infinite loops if you update state inside it.

Question

What is the cleanup function?

Click to reveal answer
Answer

If you return a function from inside useEffect, React runs it right before the component unmounts (is destroyed) or before the effect runs again. It's crucial for cleaning up event listeners or intervals to prevent memory leaks.

Question

Why shouldn't the callback be async? `useEffect(async () => {...})`

Click to reveal answer
Answer

Because `useEffect` expects the callback to either return nothing, or return a synchronous cleanup function. An `async` function returns a Promise, which confuses React and prevents cleanup from working correctly.