admin管理员组

文章数量:1295857

enter image description here

Please look at the time printed on the console.

setInterval() looks like being called twice in on interval.

I attached my code below. I'd really appreciate it if you could give me a clue to the solution.

import React, {useState} from 'react';
import AppRouter from './Router';
import {authService} from '../fbase';

function App() {
  console.log('App()');
  const [isLoggedIn, setIsLoggedIn] = useState(authService.currentUser);
  console.log(authService.currentUser);
  setInterval(() => {
    console.log(Date().toLocaleString());
    console.log('In App.js > App()');
    console.log(authService.currentUser);
  }, 5000);
  return <AppRouter isLoggedIn={isLoggedIn}></AppRouter>;
}

export default App;

enter image description here

Please look at the time printed on the console.

setInterval() looks like being called twice in on interval.

I attached my code below. I'd really appreciate it if you could give me a clue to the solution.

import React, {useState} from 'react';
import AppRouter from './Router';
import {authService} from '../fbase';

function App() {
  console.log('App()');
  const [isLoggedIn, setIsLoggedIn] = useState(authService.currentUser);
  console.log(authService.currentUser);
  setInterval(() => {
    console.log(Date().toLocaleString());
    console.log('In App.js > App()');
    console.log(authService.currentUser);
  }, 5000);
  return <AppRouter isLoggedIn={isLoggedIn}></AppRouter>;
}

export default App;

Share edited Oct 15, 2021 at 8:25 yoonjae asked Oct 15, 2021 at 8:10 yoonjaeyoonjae 911 silver badge6 bronze badges 4
  • 2 You should move calls like that into a useEffect function. – user5734311 Commented Oct 15, 2021 at 8:14
  • If you want setInterval to be called only once, you can convert App to a class based ponent, & call setInterval inside the ponentDidMount method. – Nice Books Commented Oct 15, 2021 at 8:15
  • 3 @NiceBooks Or keep using hooks and call it inside a useEffect with an empty dependency array. – user5734311 Commented Oct 15, 2021 at 8:16
  • 1 The reason is most likely <StrictMode> wrapping your <App> in index.js, see here: reactjs/docs/… – user5734311 Commented Oct 15, 2021 at 8:19
Add a ment  | 

2 Answers 2

Reset to default 5

Every time a state/prop changes your ponent will re-render. This means that everything that is not wrapped in a hook of some kind will also re-run. I'm not sure why this happens to you, maybe authService.currentUser returns two different values, an initial (probably empty) one when mounting the ponent and the correct one after it does some calculations (maybe you request the user from the back-end and it takes a while to get back the response -- some more code would be helpful here).

When the actual (correct) value will be returned from the authService to your function ponent it will re-render thus running setInterval again.

In order to make sure this never happens it's best to wrap our functionalities in a hook. If you want to run the setInterval just ONCE (when mounting the ponent) we can wrap it in an useEffect with an empty dependency array:

useEffect(() => {
  setInterval(() => {
    ...
  }, 5000)
), []}

This means the hook will only run when your ponent first mounts.

If you need to run it based on some prop change (like when isLoggedIn changes from false to true or viceversa) you can add that to the dependency array and your interval will run every time isLoggedIn state changes:

useEffect(() => {
  setInterval(() => {
    ...
  }, 5000)
}, [ isLoggedIn ])

If you only need to run that when isLoggedIn changes from false to true you can also add an if condition in your useEffect like this:

useEffect(() => {
  if (isLoggedIn) {
    setInterval(() => {
      ...
    }, 5000)
  }
}, [ isLoggedIn ])

More than that, as Jose Antonio Castro Castro mentioned, in all of the above cases you need to use a cleanup function in order to stop the interval from running indefinitely when your ponent unmounts (because it has no way of knowing to stop by itself). This is achieved by returning a function from your effect:

useEffect(() => {
  const interval = setInterval(() => {
    ...
  }, 5000)

  return () => clearInterval(interval);
), []}

Don't forget that every time your ponent feels like re-rendering, all of your constants and functions that are called directly at the root of your ponent will be re-declared / re-run again.

The useEffect, useCallback, useMemo hooks help us with exactly that, but in different ways. Choose and use them wisely!

As in the ments, you need to call setInterval when you mount the ponent, but you need to stop the timer when it is unmounted.

const App = (props) => {
  const [count, setCount] = React.useState(0)

  React.useEffect(function appRunTimer() {
    // Creates a new timer when mount the ponent.
    const timer = setInterval(() => {
      console.log(Date().toLocaleString())
      console.log('In App.js > App()')
    }, 5000)
    
    // Stops the old timer when umount the ponent.
    return function stopTimer() {
      clearInterval(timer)
    }
  }, [])
  
  console.log('DRAW')
  return (
    <button onClick={() => {setCount(count + 1)}}>
      Click to update ponent's state: You click me {count} time(s)
    </button>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
<script crossorigin src="https://unpkg./react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg./react-dom@17/umd/react-dom.production.min.js"></script>

<div id="root"></div>

本文标签: javascriptsetInterval() called twice at an interval React jsStack Overflow