javascript reactjs react-hooks

javascript - El estado no se actualiza cuando se usa el gancho de estado de React dentro de setInterval



reactjs react-hooks (4)

Dígale a React volver a renderizar cuando el tiempo cambie. optar por no

function Clock() { const [time, setTime] = React.useState(0); React.useEffect(() => { const timer = window.setInterval(() => { setTime(time + 1); }, 1000); return () => { window.clearInterval(timer); }; }, [time]); return ( <div>Seconds: {time}</div> ); } ReactDOM.render(<Clock />, document.querySelector(''#app''));

<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script> <script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script> <div id="app"></div>

Estoy probando los nuevos React Hooks y tengo un componente de reloj con un contador que se supone que aumenta cada segundo. Sin embargo, el valor no aumenta más allá de uno.

function Clock() { const [time, setTime] = React.useState(0); React.useEffect(() => { const timer = window.setInterval(() => { setTime(time + 1); }, 1000); return () => { window.clearInterval(timer); }; }, []); return ( <div>Seconds: {time}</div> ); } ReactDOM.render(<Clock />, document.querySelector(''#app''));

<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script> <script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script> <div id="app"></div>


El motivo es que la devolución de llamada que se pasa al cierre de setInterval solo tiene acceso a la variable de time en el primer procesamiento, no tiene acceso al nuevo valor de time en el procesamiento posterior porque el useEffect() no se invoca la segunda vez.

time siempre tiene el valor de 0 dentro de la setInterval llamada setInterval .

Al igual que con el setState que está familiarizado, los setState estado tienen dos formas: una donde toma el estado actualizado y la forma de devolución de llamada en la que se pasa el estado actual. Debe usar la segunda forma y leer el último valor de estado dentro del setState devolución de llamada para asegurarse de que tiene el último valor de estado antes de incrementarlo.

Bonus: Enfoques alternativos

Dan Abramov, profundiza en el tema sobre el uso de setInterval con ganchos en su publicación en el blog y proporciona formas alternativas para solucionar este problema. Recomiendo encarecidamente leerlo!

function Clock() { const [time, setTime] = React.useState(0); React.useEffect(() => { const timer = window.setInterval(() => { setTime(prevTime => prevTime + 1); // <-- Change this line! }, 1000); return () => { window.clearInterval(timer); }; }, []); return ( <div>Seconds: {time}</div> ); } ReactDOM.render(<Clock />, document.querySelector(''#app''));

<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script> <script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script> <div id="app"></div>


Una solución alternativa sería usar useReducer , ya que siempre pasará el estado actual.

function Clock() { const [time, dispatch] = React.useReducer((state = 0, action) => { if (action.type === ''add'') return state + 1 return state }); React.useEffect(() => { const timer = window.setInterval(() => { dispatch({ type: ''add'' }); }, 1000); return () => { window.clearInterval(timer); }; }, []); return ( <div>Seconds: {time}</div> ); } ReactDOM.render(<Clock />, document.querySelector(''#app''));

<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script> <script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script> <div id="app"></div>


useEffect función useEffect se evalúa solo una vez en el montaje de componentes cuando se proporciona una lista de entrada vacía.

Una alternativa a setInterval es establecer un nuevo intervalo con setTimeout cada vez que se actualiza el estado:

const [time, setTime] = React.useState(0); React.useEffect(() => { const timer = setTimeout(() => { setTime(time + 1); }, 1000); return () => { clearTimeout(timer); }; }, [time]);

El impacto en el rendimiento de setTimeout es insignificante y generalmente se puede ignorar. A menos que el componente sea sensible al tiempo hasta el punto en el que los tiempos de espera configurados recientemente causen efectos no deseados, los setInterval y setTimeout son aceptables.