My React app uses setTimeout()
and setInterval()
. Inside them, I need to access the state value. As we know, closures are bound to their context once created, so using state values in setTimeout()
/ setInterval()
won't use the newest value.
Let's keep things simple and say my component is defined as such:
import { useState, useEffect, useRef } from 'react';
const Foo = () => {
const [number, setNumber] = useState(0);
const numberRef = useRef(number);
// Is this common? Any pitfalls? Can it be done better?
numberRef.current = number;
useEffect(
() => setInterval(
() => {
if (numberRef.current % 2 === 0) {
console.log('Yay!');
}
},
1000
),
[]
);
return (
<>
<button type="button" onClick={() => setNumber(n => n + 1)}>
Add one
</button>
<div>Number: {number}</div>
</>
);
};
In total I came up with 3 ideas how to achieve this, is any of them a recognized pattern?
Assigning state value to ref on every render, just like above:
numberRef.current = number;
The benefit is very simplistic code.
Using
useEffect()
to register changes ofnumber
:useEffect( () => numberRef.current = number, [number] );
This one looks more React-ish, but is it really necessary? Doesn't it actually downgrade the performance when a simple assignment from point #1 could be used?
Using custom setter:
const [number, setNumberState] = useState(0); const numberRef = useRef(number); const setNumber = value => { setNumberState(value); numberRef.current = value; };
Is having the same value in the state and the ref a common pattern with React? And is any of these 3 ways more popular than others for any reason? What are the alternatives?