Tengo este código aquí:
export default function App() { const [count, setCount] = useState(0); const [step, setStep] = useState(0); const reff = useRef(0); useEffect(() => { const id = setInterval(() => { setCount((c) => { console.log({ step }, "reff.current before incr ", reff.current); reff.current = reff.current + step; console.log("ref.curret after incr ", reff.current); return c + step; }); }, 6000); return () => clearInterval(id); }, [step]); return ( <> <h1>Count:{count}</h1> <h1>Step: {step}</h1> <input type="button" onClick={(e) => setStep((prevStep) =>(prevStep + 1))} value="+ step" /> </> ); }Puede verlo aquí: https://codesandbox.io/s/gracious-roentgen-31ntb?file=/src/App.js:0-817
Tenga en cuenta que estoy incrementando la referencia dentro de la devolución de llamada setCount
Cuando el componente se carga, se inicia un setInterval. Si vuelvo a activar useEffect incrementando el paso antes de que pasen 6 segundos , borra setInterval y crea uno nuevo.
Y ahora, si no incremento el paso nuevamente y espero 6 segundos, veo step: 1 reff.current before incr, 0 por primera vez que se llama la devolución de llamada setInterval.
Después de incrementar en la siguiente línea ( reff.current = reff.current + step ), veo "ref.curret after incr ", 1
Cuando se vuelve a llamar a la devolución de llamada setInterval después de 6 segundos, veo
step: 1 reff.current before incr, 2 //how did this become 2
No entiendo cómo el valor de reff.current es 2.
Esto solo sucede cuando incremento el paso (que borra el primer intervalo). Si configuro el paso inicial en 1 y no lo incremento, veo los valores esperados. . Acabo de comprobar de nuevo. No funciona como se esperaba.
No puedo entender por qué el valor de reff.current es 2 cuando la devolución de llamada setInterval se llama por segunda vez.
Vaya al enlace de sandbox y
Haga clic en el botón + paso una vez
Abra la consola y vea
log 1:{step: 1} "reff.current before incr "0 log 2: ref.curret after incr 1 //second time callback is called log 3: {step: 1} "reff.current before incr "2 //this should be 1 log 4: ref.curret after incr 3```Su problema ocurre porque está realizando un efecto secundario (actualizando reff.current ) dentro de la función de actualización pasada a setCount .
Los documentos de reacción beta dicen que:
Las funciones de actualización se ejecutan durante la representación, por lo que las funciones de actualización deben ser puras y solo devolver el resultado. No intente establecer el estado desde el interior de ellos ni ejecutar otros efectos secundarios. En modo estricto, React ejecutará cada función de actualización dos veces (pero descartará el segundo resultado) para ayudarlo a encontrar errores.
Está ejecutando su aplicación en StrictMode . Para comprobarlo comentalo:
const rootElement = document.getElementById('root'); ReactDOM.render( // <StrictMode> <App />, // </StrictMode>, rootElement );y el problema ya no pasa.
No estoy seguro de la razón exacta por la que se incrementó el valor, pero usarlo directamente en lugar de regresar de una función funcionó como se esperaba. También funciona para un mayor incremento de pasos.
import { useEffect, useRef, useState } from "react"; import "./styles.css"; export default function App() { const [count, setCount] = useState(0); const [step, setStep] = useState(0); const reff = useRef(0); useEffect(() => { const id = setInterval(() => { console.log({ step }, "reff.current before incr ", reff.current); debugger; // ------ modified here let newVal = reff.current + step; reff.current = newVal; console.log("ref.curret after incr ", reff.current); setCount(newVal); // ------ modified here }, 6000); return () => clearInterval(id); }, [step]); return ( <> <h1>Count:{count}</h1> <h1>Step: {step}</h1> <input type="button" onClick={(e) => { e.preventDefault(); setStep((prevStep) => { console.log(prevStep); return Number(prevStep) + 1; }); }} value="+ step" /> </> ); }