ingrese la descripción de la imagen aquí
Mire la hora impresa en la consola.
setInterval() parece ser llamado dos veces en el intervalo.
Adjunto mi código a continuación. Te agradecería mucho si me pudieras dar una pista de la solución.
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;Cada vez que un estado/accesorio cambie, su componente se volverá a renderizar. Esto significa que todo lo que no esté envuelto en un gancho de algún tipo también se volverá a ejecutar. No estoy seguro de por qué le sucede esto, tal vez authService.currentUser devuelve dos valores diferentes, uno inicial (probablemente vacío) al montar el componente y el correcto después de que hace algunos cálculos (tal vez solicite al user desde atrás) end y lleva un tiempo obtener la respuesta; aquí sería útil un poco más de código).
Cuando el valor real (correcto) sea devuelto desde authService a su componente de función, se volverá a procesar y ejecutará setInterval nuevamente.
Para asegurarnos de que esto nunca suceda, es mejor envolver nuestras funcionalidades en un gancho. Si desea ejecutar setInterval solo UNA VEZ (al montar el componente), podemos envolverlo en un useEffect con una matriz de dependencia vacía:
useEffect(() => { setInterval(() => { ... }, 5000) ), []}Esto significa que el enlace solo se ejecutará cuando su componente se monte por primera vez.
Si necesita ejecutarlo en función de algún cambio de propiedad (como cuando isLoggedIn cambia de false a true o viceversa), puede agregarlo a la matriz de dependencias y su intervalo se ejecutará cada vez que cambie el estado isLoggedIn :
useEffect(() => { setInterval(() => { ... }, 5000) }, [ isLoggedIn ]) Si solo necesita ejecutar eso cuando isLoggedIn cambia de false a true , también puede agregar una condición if en su useEffect como esta:
useEffect(() => { if (isLoggedIn) { setInterval(() => { ... }, 5000) } }, [ isLoggedIn ])Más que eso, como mencionó José Antonio Castro Castro , en todos los casos anteriores, debe usar una función de limpieza para evitar que el intervalo se ejecute indefinidamente cuando su componente se desmonta (porque no tiene forma de saber detenerse por sí mismo) . Esto se logra devolviendo una función de su efecto:
useEffect(() => { const interval = setInterval(() => { ... }, 5000) return () => clearInterval(interval); ), []}No olvide que cada vez que su componente tenga ganas de volver a renderizar, todas sus constantes y funciones que se llaman directamente en la raíz de su componente se volverán a declarar/ejecutar nuevamente.
Los useEffect , useCallback , useMemo nos ayudan exactamente con eso, pero de diferentes maneras. ¡Elige y úsalos sabiamente!
Como en los comentarios, debe llamar a setInterval cuando monte el componente, pero debe detener el temporizador cuando se desmonte.
const App = (props) => { const [count, setCount] = React.useState(0) React.useEffect(function appRunTimer() { // Creates a new timer when mount the component. const timer = setInterval(() => { console.log(Date().toLocaleString()) console.log('In App.js > App()') }, 5000) // Stops the old timer when umount the component. return function stopTimer() { clearInterval(timer) } }, []) console.log('DRAW') return ( <button onClick={() => {setCount(count + 1)}}> Click to update component's state: You click me {count} time(s) </button> ); }; ReactDOM.render(<App />, document.getElementById("root")); <script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script> <div id="root"></div>