~/hackweb.dev
Side Effects with useEffect
Quiz
...

Side Effects with useEffect

intermediate · updated Tue Sep 08 2026Contribute

Run side effects like data fetching and subscriptions with useEffect.

STEP 1 · useEffect Lifecycle

useEffect LifecycleStep 1 / 7
MountRenderEffect RunsCleanupDep Changes?

Component mounts for the first time — appears in the DOM.

Side Effects with useEffect

useEffect runs code after render to handle side effects.

Basic Syntax

import { useEffect } from "react";

function App() {
  useEffect(() => {
    console.log("Component rendered");
  });

  return <div>Hello</div>;
}

The function runs after every render by default.

Dependency Array

Control when the effect runs:

useEffect(() => {
  console.log("Count changed:", count);
}, [count]);
  • Empty [] — runs once on mount
  • [a, b] — runs when a or b changes
  • No array — runs after every render

Cleanup Function

Return a function to clean up resources:

useEffect(() => {
  const id = setInterval(() => console.log("tick"), 1000);
  return () => clearInterval(id);
}, []);

Always clean up subscriptions and timers.

Fetching Data

useEffect(() => {
  async function fetchData() {
    const res = await fetch("/api/users");
    const data = await res.json();
    setUsers(data);
  }
  fetchData();
}, []);

Call the async function inside the effect, not directly.

Common Pitfalls

// Infinite loop — mutating state inside effect
useEffect(() => {
  setCount(count + 1);
}, [count]); // keeps triggering

Best Practices

  1. Always specify a dependency array
  2. Clean up intervals, subscriptions, and event listeners
  3. Use async functions inside effects properly
  4. Avoid effects that just set state — derive instead

Common Mistakes

  1. Missing dependencies causes stale values
  2. No cleanup causes memory leaks
  3. Calling async functions directly in useEffect
  4. Updating state without a condition leads to infinite loops