• Mail us
  • Book a Meeting
  • Call us
  • Chat with us

ReactJS

How to Handle useEffect Running Multiple Times in React


Introduction

If you are using React, you may have noticed that the useEffect hook runs multiple times on mount, even when it has an empty dependency array. This can be confusing, especially if you are logging values or making API calls inside useEffect. In this blog, we will explore why this happens across different React versions and how to handle it effectively.

 

Understanding the Issue

Let's consider a simple React counter component:

import { useState, useEffect } from "react";const Counter = () => { const [count, setCount] = useState(5); useEffect(() => { console.log("rendered", count); }, [count]); return ( <div> <h1>Counter</h1> <div>{count}</div> <button onClick={() => setCount(count + 1)}>click to increase</button> </div> );};export default Counter;

 

Expected Behavior

When the component mounts, we expect useEffect to run once and log rendered 5 in the console.

Actual Behavior

However, depending on the React version and environment (development vs. production), useEffect might run multiple times on mount.

 

Why Does This Happen in Different React Versions?

  • React 16 and 17 In React 16 and 17, useEffect runs once on mount if the dependency array is empty ([]). However, if strict mode is enabled, you won't see the double execution issue.
  • React 18 React 18 introduced Strict Mode behavior, where components are mounted, unmounted, and then remounted immediately. This results in useEffect running twice in development mode to help catch side effects and improve reliability. In production, useEffect runs only once as expected.
  • React 19 (Expected Behavior) React 19 is expected to retain strict mode behavior but might introduce optimizations to avoid unnecessary re-executions. If strict mode remains active, the double execution issue could still occur.

 

How to Handle This Behavior

1. Disable Strict Mode (Not Recommended)

You can disable Strict Mode by modifying main.jsx or index.js:

import React from "react";import ReactDOM from "react-dom/client";import Counter from "./Counter";ReactDOM.createRoot(document.getElementById("root")).render( // Remove <React.StrictMode> <Counter />);

 

However, this is not recommended because Strict Mode helps catch potential bugs.

2. Use a Ref to Track First Render

To prevent useEffect from running on the first mount, you can use a useRef to track the first render:

import { useState, useEffect, useRef } from "react";const Counter = () => { const [count, setCount] = useState(5); const firstRender = useRef(true); useEffect(() => { if (firstRender.current) { firstRender.current = false; return; } console.log("rendered", count); }, [count]); return ( <div> <h1>Counter</h1> <div>{count}</div> <button onClick={() => setCount(count + 1)}>click to increase</button> </div> );};export default Counter;

 

This ensures useEffect does not run on the first render but runs on subsequent updates.

3. Use a Cleanup Function (For API Calls)

If you are making API requests inside useEffect, you can use an AbortController to cancel requests when the component unmounts:

import { useState, useEffect } from "react";const FetchData = () => { const [data, setData] = useState(null); useEffect(() => { const controller = new AbortController(); fetch("https://api.example.com/data", { signal: controller.signal }) .then((res) => res.json()) .then((data) => setData(data)) .catch((err) => console.error("Fetch error:", err)); return () => controller.abort(); }, []); return <div>{data ? JSON.stringify(data) : "Loading..."}</div>;};export default FetchData;

 

This prevents unnecessary API calls due to Strict Mode re renders.

Conclusion

  • In React 16 and 17, useEffect runs only once on mount with an empty dependency array.

  • In React 18, useEffect runs twice on mount due to Strict Mode.

  • React 19 is expected to retain Strict Mode behavior, so double execution may still occur.

  • You can handle this behavior by:

    • Keeping Strict Mode enabled for debugging.

    • Using useRef to prevent useEffect from running on the first render.

    • Using an AbortController for API requests inside useEffect.

 

  Ready to transform your business with our technology solutions? Contact Us today to Leverage Our ReactJS Expertise. 

0

Share

facebook
LinkedIn
Twitter
Mail
React

Related Center Of Excellence