frontend

React Side Effects Explained with useEffect (2025 Guide)

Updated: September 15, 2025

React Side Effects Explained with useEffect (2025 Guide)

In React, side effects are actions that occur outside the normal rendering process. Examples include fetching data, updating the DOM directly, working with APIs, or syncing with browser storage. React provides the useEffect hook to handle these tasks.


When Does React Run useEffect?

  • On mount (first render).
  • On updates (when dependencies change).
  • On unmount (cleanup).

React never runs useEffect during server-side rendering — it only runs in the browser after the component mounts. (Server Components can't use useEffect at all — hooks are a Client Component feature.)


The Dependency Array

The second argument to useEffect is the dependency array.

  • [] → Run only once after mount.
  • [value] → Run when value changes.
  • Omit it → Run after every render.

Example: useEffect with State Dependency

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

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Count: ${count}`;
    return () => {
      document.title = "React App";
    };
  }, [count]); // effect runs whenever "count" changes

  return (
    <button onClick={() => setCount(count + 1)}>
      Increase Count ({count})
    </button>
  );
}

StrictMode and Double-Invoked Effects

In development, wrapping your app in <React.StrictMode> makes React intentionally run each effect's setup and cleanup one extra time on mount — setup → cleanup → setup — specifically to surface effects that are missing proper cleanup. This has been StrictMode's behavior since React 18 and continues in React 19.

  • It only happens in development builds; production builds run each effect once, as written.
  • If double-invocation breaks your component (for example, duplicate API calls or subscriptions), the fix is almost always a missing or incomplete cleanup function — not disabling StrictMode.
  • The counter example above will log two mount/cleanup cycles for document.title in development under StrictMode before settling on the correct value; that's expected, not a bug.

Side Effect Examples

  • API calls (fetching data)
  • WebSockets (listening to live updates)
  • Local Storage (persisting state)
  • Syncing multiple states

Best Practices (2025)

  1. Always declare dependencies (use ESLint plugin react-hooks/exhaustive-deps).
  2. Clean up effects when possible (return () => { ... }).
  3. Avoid heavy computations inside useEffect → use memoization instead.
  4. For async logic, use an async function inside useEffect.
useEffect(() => {
  let isActive = true;

  async function fetchData() {
    const res = await fetch("/api/data");
    if (isActive) {
      // only update if still mounted
    }
  }

  fetchData();
  return () => {
    isActive = false;
  };
}, []);

Conclusion

useEffect is the cornerstone of managing side effects in React functional components. By mastering dependency arrays, cleanup functions, and modern patterns, you’ll write React apps that are predictable, performant, and easier to maintain.