Quiero detener el bucle cuando stopState sea verdadero.
El estado de parada se actualiza cuando hago clic en el botón de parada. pero dentro del método startIncrement, stopState siempre es falso.
Este es mi código:
function App() { const [num, setNum] = useState(0) const [stop, setStop] = useState(false) function Delay(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } async function startIncrement(){ for(let i=0; i<100; i++){ console.log(stop) // always false even when i click the stop button if(stop) i = 2000; setNum(i) await Delay(1000) } } return ( <main> <p>stop: {stop ? "true" : "false"}</p> <p onClick={startIncrement}>{num}</p> <button onClick={() => setStop(true)}>stop</button> </main> ); }startIncrement() , en el momento de la ejecución, y en esa fase particular de renderizado, el valor de stop es falso;num , la página se procesa y continúa... (ejecuta el código debajo, puedes ver las impresiones de la consola "¡se procesa!")setStop(true) , y en este render particular, stop === true. Sin embargo, estos dos pasos involucran dos renderizados diferentes, y el estado de cada renderizado y todo lo demás (accesorios, efectos...), no se afectan entre sí, por lo tanto, su valor de stop en la función nunca cambia.
Aquí hay una alternativa para lograr lo mismo:
export default function App() { const [num, setNum] = useState(0) const [stop, setStop] = useState(null) console.log("renders!") useEffect(() => { const run = setInterval(() => { if(stop === false && num < 100) setNum(num + 1); }, 1000); return () => clearInterval(run) // clean up function }, [num, stop]); return ( <main> <p>stop: {stop ? "true" : "false"}</p> <p>{num}</p> <button onClick={() => setStop(false)}>start</button> <button onClick={() => setStop(true)}>stop</button> </main> ); }La función de limpieza en useEffect puede verse como una función de "deshacer".
Sandbox aquí , puede leer más sobre los efectos secundarios de esta publicación en el blog de Dan Abramov