DEV Community

Rakesh Daniel
Rakesh Daniel

Posted on

useEffect in React

useEffect is a React Hook used for side effects — things like API calls, timers, event listeners, and updating the document title. Its second argument, the dependency array, controls when the effect runs.

1. Run Only Once

useEffect(() => {
  console.log("Component Mounted");
}, []);

Enter fullscreen mode Exit fullscreen mode

Dependency array: .

Why? An empty array tells React that the effect does not depend on any state or prop, so it runs only after the component first appears on screen.

2. Run on Every Render

useEffect(() => {
  console.log("Rendered");
});
Enter fullscreen mode Exit fullscreen mode

No dependency array is provided.

React runs this effect after every render — on first mount and after every state or prop change.

3. Run When State Changes

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

useEffect(() => {
  console.log("Count changed");
}, [count]);
Enter fullscreen mode Exit fullscreen mode
  • Dependency array: [count].
  • The effect runs on mount and again whenever count changes.
  • If you use [] instead, "Count changed" prints only once on mount — not when count updates

4. Two States, One Effect

const [count, setCount] = useState(0);
const [name, setName] = useState("");

useEffect(() => {
  console.log("count or name changed");
}, [count, name]);
Enter fullscreen mode Exit fullscreen mode
  • Dependency array: [count, name].
  • The effect runs when either count or name changes.
  • If only [count] is added, changing name will not run this effect. Also, never omit a value used inside the effect — it can cause stale-value bugs.
  1. Derived Logic

<p>{count % 2 === 0 ? "Even Number" : "Odd Number"}</p>

  • Put this logic inside render, not inside useEffect.
  • Why? It is directly calculated from existing state. React can re-render and show the correct value automatically. useEffect is for side effects outside React’s normal rendering flow, such as API calls or DOM listeners.

6. API Call on Mount

useEffect(() => {
  fetch("https://jsonplaceholder.typicode.com/users")
    .then((res) => res.json())
    .then((data) => setUsers(data));
}, []);
Enter fullscreen mode Exit fullscreen mode
  • We use [] because users should be fetched only once when the component loads.
  • If you remove [], the API call runs after every render, causing unnecessary network requests
  1. Infinite Loop Trap
useEffect(() => {
  setCount(count + 1);
}, [count]);
Enter fullscreen mode Exit fullscreen mode

This creates an infinite loop.

Why? The effect runs → count changes → React re-renders → dependency count changed → effect runs again → count changes again. This repeats forever

  1. Fix the Infinite Loop

If you want to increment only once:

useEffect(() => {
  setCount((prev) => prev + 1);
}, []);
Enter fullscreen mode Exit fullscreen mode
  • Use an empty dependency array [].
  • Use the functional update setCount((prev) => prev + 1) so you always update from the latest state value.
  • Do not put count in the dependency array if the effect itself changes count.

9. Document Title Sync

useEffect(() => {
  document.title = count;
}, [count]);
Enter fullscreen mode Exit fullscreen mode
  • Put count inside the dependency array.
  • Why? The effect uses count, and the title should update every time count changes.

10. Cleanup Basic

useEffect(() => {
  window.addEventListener("click", handleClick);
});
Enter fullscreen mode Exit fullscreen mode

Problem
Every render adds another click listener. Old listeners are never removed, which can cause memory leaks and repeated function calls.

Correct Way

useEffect(() => {
  const handleClick = () => {
    console.log("Window clicked");
  };

  window.addEventListener("click", handleClick);

  return () => {
    window.removeEventListener("click", handleClick);
  };
}, []);
Enter fullscreen mode Exit fullscreen mode
  • The returned function is the cleanup function.
  • Cleanup runs before the next effect execution and when the component unmounts.

Quick Dependency Table

Dependency array When effect runs
No array After every render react
[] Once after first render / mount react+1
[count] On mount and when count changes react
[count, name] On mount and when either changes react

Simple Rule to Remember

  • Need something once when page loads? Use [].
  • Need something whenever a value changes? Add that value: [value].
  • Need something after every render? Omit the array.
  • Added a listener, timer, or subscription? Always return a cleanup function.

Top comments (0)