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.