I have this code here:
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"
/>
</>
);
}
You can view it here: https://codesandbox.io/s/gracious-roentgen-31ntb?file=/src/App.js:0-817
Note that I'm incrementing ref inside setCount
callback
When the component loads a setInterval is started. If I trigger the useEffect again by incrementing step before 6 seconds pass, it clears the setInterval and creates a new one.
And now if I don't increment step again and wait for 6 seconds, I see step: 1 reff.current before incr, 0
for the first time the setInterval callback is called.
After incrementing in the next line(reff.current = reff.current + step
) I see "ref.curret after incr ", 1
When the setInterval callback is called again after 6 seconds, I see
step: 1 reff.current before incr, 2 //how did this become 2
I don't understand how the value of reff.current
is 2.
This only happens when I increment the step(which clears the first interval). If I set the initial step to 1 and don't increment it, I see expected values.. Just checked again. It doesn't work as expected.
I can't understand why the value of reff.current
is 2 when the setInterval callback is called the second time.
Go to the sandbox link and
Click on the +step button once
Open the console and see
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```
Your problem happens because you are performing a side effect (updating reff.current
) inside the updater function passed to the setCount
.
The beta react docs say that:
Updater functions run during rendering, so updater functions must be pure and only return the result. Don’t try to set state from inside of them or run other side effects. In Strict Mode, React will run each updater function twice (but discard the second result) to help you find mistakes.
You are running your app in StrictMode
. To verify it comment it:
const rootElement = document.getElementById('root');
ReactDOM.render(
// <StrictMode>
<App />,
// </StrictMode>,
rootElement
);
and the problem doesn't happen anymore.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With