React Hooks: useEffect() is called twice even if an empty array is used as an argument
Asked Answered
E

23

684

I am writing code so that before the data is loaded from DB, it will show loading message, and then after it is loaded, render components with the loaded data. To do this, I am using both useState hook and useEffect hook. Here is the code:

The problem is, useEffect is triggered twice when I check with console.log. The code is thus querying the same data twice, which should be avoided.

Below is the code that I wrote:

import React from 'react';
import './App.css';
import {useState,useEffect} from 'react';
import Postspreview from '../components/Postspreview'

const indexarray=[]; //The array to which the fetched data will be pushed

function Home() {
   const [isLoading,setLoad]=useState(true);
   useEffect(()=>{
      /*
      Query logic to query from DB and push to indexarray
      */
          setLoad(false);  // To indicate that the loading is complete
    })
   },[]);
   if (isLoading===true){
       console.log("Loading");
       return <div>This is loading...</div>
   }
   else {
       console.log("Loaded!"); //This is actually logged twice.
       return (
          <div>
             <div className="posts_preview_columns">
             {indexarray.map(indexarray=>
             <Postspreview
                username={indexarray.username}
                idThumbnail={indexarray.profile_thumbnail}
                nickname={indexarray.nickname}
                postThumbnail={indexarray.photolink}
             />
             )}
            </div>
         </div>  
         );
    }
}

export default Home;

Why it is called twice, and how to fix the code properly?

Excited answered 10/3, 2020 at 13:40 Comment(5)
you say when you check the console.log but there is no console.logHalverson
Deleted them initially because I pretty much explained what happened, but added them back for clarity as per your comment.Excited
My Solution is https://mcmap.net/q/64782/-my-react-component-is-rendering-twice-because-of-strict-mode shared here. I guess help to you.Piane
For those are using React 18 #72238675.Philosophy
I am sure this is happening because of <React.Strict>. try replacing <React.Strict><Home></React.Strict> with only <Home>. strict mode renders component twice on local and once on production.Terrilyn
H
1106

Put the console.log inside the useEffect

Probably you have other side effects that cause the component to rerender but the useEffect itself will only be called once. You can see this for sure with the following code.

useEffect(()=>{
      /*
      Query logic
      */
      console.log('i fire once');
},[]);

If the log "i fire once" is triggered more than once it means your issue is one of 3 things.

This component appears more than once in your page

This one should be obvious, your component is in the page a couple of times and each one will mount and run the useEffect

Something higher up the tree is unmounting and remounting

The component is being forced to unmount and remount on its initial render. This could be something like a "key" change happening higher up the tree. you need to go up each level with this useEffect until it renders only once. then you should be able to find the cause or the remount.

React.Strict mode is on

StrictMode renders components twice (on dev but not production) in order to detect any problems with your code and warn you about them (which can be quite useful).

This answer was pointed out by @johnhendirx and written by @rangfu, see link and give him some love if this was your problem. If you're having issues because of this it usually means you're not using useEffect for its intended purpose. There's some great information about this in the docs you can read that here

Halverson answered 10/3, 2020 at 13:54 Comment(7)
But it is not specified anywhere that StrictMode cause useEffect to run twice too. Strict Mode is used to detect if we are doing side effect in any function which should be pure so only those functions that needed to be pure are run twice but as useEffect can contain side effects it should be run twice in Strict Mode. Any documentation around this stating that useEffect is also run twice?Biped
For a more detailed answer about Strict Mode #72238675.Philosophy
Man, strict mode should issue a warning that it does this in dev environments. I really wish it wouldn't do that silentlyLarine
React.Strictmode yea that explains why my firebase reads were exhausting so muchNu
For those who are using NextJS, check on your next.config.js: [module.exports = { reactStrictMode: true, <== False to disable strict mode }]Punchy
@Punchy you should not disable this, you should change your code to work better in strict mode.Halverson
@JoeLloyd I'm just pointing here out how you can disable StrictMore with NextJS. My app has the strict mode = truePunchy
H
322

Remove <React.StrictMode> from index.js This code will be

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

this

root.render(
    <App />
);

React StrictMode renders components twice on dev server

Hera answered 23/4, 2022 at 18:51 Comment(5)
Oh man, StrictMode was the culprit. Yeasin it would be great if you could give more explaination about it tooTusche
@YashPokar According to my understanding , React StrictMode renders the components twice as mentioned. Why? To identify components with unsafe life cycle as you can see here -> reactjs.org/docs/strict-mode.html#identifying-unsafe-lifecycles . In other words to make sure the components are resilient to mounting and unmounting The best solution would be writing the cleanup function if you are using useEffect. If you really have some heavy stuffs in useEffect , maybe move those out from useEffect all together.Hairpiece
Removing StrictMode is the last thing to do. It's there for a purpose. See #72238675.Philosophy
I was going to say that @YoussoufOumar , StrictMode shouldn't be tampered with. Thanks for pointing that out.Triploid
So... what is the solution then? StrictMode causes a bug which is a huge problem.Trichotomy
H
67

You are most likely checking the issue on a dev environment with strict mode enabled. To validate this is the case, search for <React.StrictMode> tag and remove it, or build for production. The double render issue should be gone. From React official documentation

Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:

  • Functions passed to useState, useMemo, or useReducer
  • [...]

Strict Mode - Reactjs docs

Similar question here My React Component is rendering twice because of Strict Mode

Haunch answered 21/2, 2021 at 17:3 Comment(2)
But useEffect is not on the list of hooks that get double-invoked. In fact, useEffect isn't mentioned in the documentation of Strict Mode. In my app it seems like double invoking happens in some cases and not others.Thirddegree
I didn't check if useEffect is mentioned in the docs, but it lives inside the render function, so the comments should apply to it too.Microsome
T
46

As others have already pointed out, this happens most likely due to a Strict Mode feature introduced in React 18.0.

I wrote a blog post that explains why this is happening and what you can do to work around it.

But if you just want to see the code, here you go:

const initialized = useRef(false)

useEffect(() => {
  if (!initialized.current) {
    initialized.current = true

    // My actual effect logic...
    ...
  }
}, [])

Or as a re-usable hook:

import type { EffectCallback } from "react"
import { useEffect, useRef } from "react"

export function useOnMountUnsafe(effect: EffectCallback) {
  const initialized = useRef(false)

  useEffect(() => {
    if (!initialized.current) {
      initialized.current = true
      effect()
    }
  }, [])
}

Please keep in mind that you should only resort to this solution if you absolutely have to!

Timeconsuming answered 15/1, 2023 at 15:29 Comment(3)
Seconding / underscoring that this should be a fallback solution to debug — not a 'solution'.Enrobe
Why use it as last resort? .. Is there something wrong using the useRef as a sentinel flag?Detrital
This doesn't work because Strictmode only runs in development environments (i.e. npm start). When you build (npm run build), then strictmode doesn't run anymore, the useEffect with an empty dep list will only run once not twice.Barbecue
C
28

Please check your index.js

  <React.StrictMode>
    <App />
  </React.StrictMode>

Remove the <React.StrictMode> wrapper you should now fire once

root.render(
    <App />
);
Covariance answered 22/4, 2022 at 3:28 Comment(1)
Removing StrictMode is the last thing to do. It's there for a purpose. See #72238675Philosophy
B
26

if you are using Next js, change reactStrictMode from "true" to false :

add this to your next.config.js

reactStrictMode: false,
Biomass answered 19/8, 2022 at 11:16 Comment(3)
reactStrictMode is by default true, and setting it to false would solve the problem, but also we should keep in mind as nextjs docs recommends, keeping reactStrictMode true, can also be beneficial: nextjs.org/docs/api-reference/next.config.js/react-strict-modeLukasz
Removing StrictMode is the last thing to do. It's there for a purpose. See #72238675Philosophy
work for me in nextjs, thankCoalesce
P
26

react root > index.js > remove <React.StrictMode> wrapper

Pentathlon answered 8/9, 2022 at 2:19 Comment(2)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Shela
This worked for me. I'm just gonna up vote it up.Souter
U
19

It is the feature of ReactJS while we use React.StrictMode. StrictMode activates additional checks and warnings for its descendants nodes. Because app should not crash in case of any bad practice in code. We can say StrictMode is a safety check to verify the component twice to detect an error.

You will get this <React.StricyMode> at root of the component.

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

if you want to restrict components to render twice, You can remove <React.StrictMode> and check it. But It is necessary to use StrictMode to detect a run time error in case of bad code practice.

Ulceration answered 27/5, 2022 at 13:2 Comment(0)
A
10

I have found a very good explanation behind twice component mounting in React 18. UseEffect called twice in React

Note: In production, it works fine. Under strict mode in the development environment, twice mounting is intentionally added to handle the errors and required cleanups.

Amandy answered 26/8, 2022 at 19:13 Comment(0)
B
10

React strict mode renders components twice on the dev server.

So, you have to remove StrictMode from index.js. Your index.js current code can be below.

root.render(
 <React.StrictMode>
 <App />
 </React.StrictMode>
);

After removing StrictMode it should look like below

root.render(
 <App />
);
Boeotian answered 24/6, 2023 at 12:4 Comment(0)
S
8

The new React docs (currently in beta) have a section describing precisely this behavior:

How to handle the Effect firing twice in development

From the docs:

Usually, the answer is to implement the cleanup function. The cleanup function should stop or undo whatever the Effect was doing. The rule of thumb is that the user shouldn’t be able to distinguish between the Effect running once (as in production) and a setup → cleanup → setup sequence (as you’d see in development).

So this warning should make you double check your useEffect, usually means you need to implement a cleanup function.

Supermarket answered 10/11, 2022 at 22:8 Comment(0)
V
5

I'm using this as my alternative useFocusEffect. I used nested react navigation stacks like tabs and drawers and refactoring using useEffect doesn't work on me as expected.

import React, { useEffect, useState } from 'react'
import { useFocusEffect } from '@react-navigation/native'

const app = () = {

  const [isloaded, setLoaded] = useState(false)


  useFocusEffect(() => {
      if (!isloaded) {
        console.log('This should called once')

        setLoaded(true)
      }
    return () => {}
  }, [])

}

Also, there's an instance that you navigating twice on the screen.

Vinificator answered 14/1, 2021 at 16:40 Comment(1)
this not workingJuieta
S
4

It is strict mode in my case. Remove strict mode component at index.tsx or index.jsx

Scarlet answered 28/8, 2022 at 5:54 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Shela
P
4

I found a rather useful solution which is 100% react.

In my case I have a token which I'm using to make a POST request which logs out my current user.

I'm using a reducer with state like this:

export const INITIAL_STATE = {
  token: null
}

export const logoutReducer = (state, action) => {

  switch (action.type) {

    case ACTION_SET_TOKEN :

      state = {
        ...state,
        [action.name] : action.value
      };

      return state;

    default:
      throw new Error(`Invalid action: ${action}`);
  }

}

export const ACTION_SET_TOKEN = 0x1;

Then in my component I'm checking the state like this:

import {useEffect, useReducer} from 'react';
import {INITIAL_STATE, ACTION_SET_TOKEN, logoutReducer} from "../reducers/logoutReducer";

const Logout = () => {

  const router = useRouter();
  const [state, dispatch] = useReducer(logoutReducer, INITIAL_STATE);

  useEffect(() => {

    if (!state.token) {
    
      let token = 'x' // .... get your token here, i'm using some event to get my token

      dispatch(
        {
          type : ACTION_SET_TOKEN,
          name : 'token',
          value : token
        }
      );
    
    } else {
    
      // make your POST request here
      
    }
    
 }

The design is actually nice - you have the opportunity to discard your token from storage after the POST request, make sure the POST succeeds before anything. For async stuff you can use the form :

  POST().then(async() => {}).catch(async() => {}).finally(async() => {})

all running inside useEffect - works 100% and within I think what the REACT developers had in mind - this pointed out that I actually had more cleanup to do (like removing my tokens from storage etc) before everything was working but now I can navigate to and from my logout page without anything weird happening.

Propman answered 8/2, 2023 at 2:32 Comment(0)
W
3

If someone comes here using NextJS 13, in order to remove the Strict mode you need to add the following on the next.config.js file:

const nextConfig = {
  reactStrictMode: false
}
module.exports = nextConfig

When I created the project it used "Strict mode" by default that's why I must set it explicitly.

Withal answered 17/11, 2022 at 8:28 Comment(2)
Cool, but what is the downside of not running the app in "strict mode"?Jannjanna
The "strict mode" prevent you from possible issues giving you some warnings. You can check more about it here: react.dev/reference/react/StrictModeWithal
C
3

This may not be the ideal solution. But I used a workaround.

var ranonce = false;
useEffect(() => {
    if (!ranonce) {

        //Run you code

        ranonce = true
    }
}, [])

Even though useEffect runs twice code that matters only run once.

Cyrillic answered 5/1, 2023 at 9:13 Comment(0)
T
2

Not sure why you won't put the result in state, here is an example that calls the effect once so you must have done something in code not posted that makes it render again:

const App = () => {
  const [isLoading, setLoad] = React.useState(true)
  const [data, setData] = React.useState([])
  React.useEffect(() => {
    console.log('in effect')
    fetch('https://jsonplaceholder.typicode.com/todos')
      .then(result => result.json())
      .then(data => {
        setLoad(false)//causes re render
        setData(data)//causes re render
      })
  },[])
  //first log in console, effect happens after render
  console.log('rendering:', data.length, isLoading)
  return <pre>{JSON.stringify(data, undefined, 2)}</pre>
}

//render app
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

To prevent the extra render you can combine data and loading in one state:

const useIsMounted = () => {
  const isMounted = React.useRef(false);
  React.useEffect(() => {
    isMounted.current = true;
    return () => isMounted.current = false;
  }, []);
  return isMounted;
};


const App = () => {
  const [result, setResult] = React.useState({
    loading: true,
    data: []
  })
  const isMounted = useIsMounted();
  React.useEffect(() => {
    console.log('in effect')
    fetch('https://jsonplaceholder.typicode.com/todos')
      .then(result => result.json())
      .then(data => {
        //before setting state in async function you should
        //  alsways check if the component is still mounted or
        //  react will spit out warnings
        isMounted.current && setResult({ loading: false, data })
      })
  },[isMounted])
  console.log(
    'rendering:',
    result.data.length,
    result.loading
  )
  return (
    <pre>{JSON.stringify(result.data, undefined, 2)}</pre>
  )
}

//render app
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Torre answered 10/3, 2020 at 13:59 Comment(10)
For someone that is having issues with having mutiple undesire runs of the useEffect hook I DONT recommend adding extra complexity by using a custom hook like that (useIsMounted).He should understand why is that happening and fix it accordingly.Bard
@LuisGurmendez Recommending checking if the component is still mounted before setting state with asynchronous result is sound advice. If op has unexpected calls to the effect then the code posted in the question does not demonstrate that.Torre
If that’s the case he can use the return funcion of the useEffect callback to make appropiate cleanupsBard
es.reactjs.org/blog/2015/12/16/ismounted-antipattern.htmlBard
@LuisGurmendez Class method isMounted has nothing to do with custom hook called useIsMounted I could have called that hook anything as long as it starts with use. They are completely different things.Torre
If you read the document it speaks as components in general, the examples might be using class components but is the same idea. As they say having reference to unmounted components is a code smell. Here is a quote that I find interesting and I hope you too: "Ideally, any callbacks should be canceled in componentWillUnmount, prior to unmounting."Bard
@LuisGurmendez There is no reference to a component, there is a reference to a setter created with useState, it's a different thing. It is also cleaned up when the xhr promise resolves or rejects this variable is out of scope. The fetch method returns a promise and cannot be cancelled. However; if you have a better solution I'd love to see it. You should probably put it on github since the useIsMounted is there and used many times.Torre
There is a reference to the component in the scope of the callback. It is not clean up when the "xhr promise resolves or rejects" that is not the idea of cleanup. The idea is that that request is cancelled, and thus there is no resolved/reject of the promise. And there is actually ways to abort a fetch request, take a look to this post #31062338 .Bard
The reference is in the scope of the callback as I already said, all the components functions and state are still in memory until the callback ends, that's what the React team says about memory leaks. Because all that memory is taken by a component that will no render anymore. React doesn't do magic, components are javascript functions. Cancelling requests is a good pattern. Fix the cause, not the symptom I see you remove your last comment. this was a response to that.Bard
@LuisGurmendez Where is the component then in this callback: data => { isMounted.current && setResult({ loading: false, data }) }? I see a function called setResult and an object isMounted and they will go out of scope as soon as the promise completes. You keep using language that refers to react classes like all the components functions and state are still in memory and component reference in scope but that doesn't apply here. If the fetch is broken and never completes there is a potential memory leak but if you have to take that into account you may as well not do anything.Torre
O
1

I've had this issue where something like:

const [onChainNFTs, setOnChainNFTs] = useState([]);

would trigger this useEffect twice:

useEffect(() => {
    console.log('do something as initial state of onChainNFTs changed'); // triggered 2 times
}, [onChainNFTs]);

I confirmed that the component MOUNTED ONLY ONCE and setOnChainNFTs was NOT called more than once - so this was not the issue.

I fixed it by converting the initial state of onChainNFTs to null and doing a null check.

e.g.

const [onChainNFTs, setOnChainNFTs] = useState(null);
useEffect(() => {
if (onChainNFTs !== null) {
    console.log('do something as initial state of onChainNFTs changed'); // triggered 1 time!
}
}, [onChainNFTs]);
Originative answered 30/7, 2021 at 13:52 Comment(0)
B
1

Here is the custom hook for your purpose. It might help in your case.

import {
  useRef,
  EffectCallback,
  DependencyList,
  useEffect
} from 'react';

/**
 * 
 * @param effect 
 * @param dependencies
 * @description Hook to prevent running the useEffect on the first render
 *  
 */
export default function useNoInitialEffect(
  effect: EffectCallback,
  dependancies?: DependencyList
) {
  //Preserving the true by default as initial render cycle
  const initialRender = useRef(true);

  useEffect(() => {
   
    let effectReturns: void | (() => void) = () => {};
    
    /**
     * Updating the ref to false on the first render, causing
     * subsequent render to execute the effect
     * 
     */
    if (initialRender.current) {
      initialRender.current = false;
    } else {
      effectReturns = effect();
    }

    /**
     * Preserving and allowing the Destructor returned by the effect
     * to execute on component unmount and perform cleanup if
     * required.
     * 
     */
    if (effectReturns && typeof effectReturns === 'function') {
      return effectReturns;
    } 
    return undefined;
  }, dependancies);
}

Beitris answered 26/8, 2021 at 17:50 Comment(0)
P
1

As mentioned, StrictMode is there for a purpose, and it may not always be correct to just disable strictMode to get rid of the issue.

useEffect's callback should ideally return a cleanup function, which gets called before executing the useEffect next time. With respect to the posted question, We can easily clean the array in the cleanup task, so that the array does not contain duplicate data.

 useEffect(()=>{
      // Fill the array here.
      
      // Return a cleanup callback
      return () => {
          // At this point, We can clean the array.
      } 
    
 },[]);

Providing a more concrete example from a coding project, where i am raising a setInterval call and registering for some notitification.

 useEffect(() => {
    let notificationId = StoreManager.registerForUpdateNotification(() => {
      setStore(StoreManager.getInstance());
    });
    let syncIntervalId = setInterval(() => {
      StoreManager.syncStoreWithCloudInstance();
    }, 1 * 30 * 1000);


    return () => {
      StoreManager.deregisterForUpdateNotification(notificationId);
      clearInterval(syncIntervalId);
    }
  }, []);
Prey answered 3/6, 2023 at 6:12 Comment(0)
M
0

When you are running React in development mode. It will sometimes run twice. Test it in prod environment and your useEffect will only run once.

Mercedes answered 18/7, 2022 at 10:42 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Shela
F
-2

in my case just i change useEffect(()=>{ //.... }) to useEffect(()=>{ //.... },[])

Fighterbomber answered 10/7, 2023 at 10:12 Comment(0)
G
-4

I used CodeSandbox and removing prevented the issue.

CodeSandbox_sample

Gottwald answered 21/10, 2022 at 18:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.