Lesson 6. Get the current state when dealing with async code 2. Read the state’s current value using a ref
The solution we saw in the previous section worked very well because we wanted to update a state based on its current value. But what if we want to get the state’s current value, not to update it, but to update another state?
Result
In this example, if the input’s value is “Hello” and you click the button, then
update the value to “Good bye”, you’ll notice that the value of copy will be
“Hello”. But we want to copy the current state, not the one at the time I click
the button.
In that case, using setCopy’s syntax with a callback won’t help us, because we
want to get original’s current value, not copy’s one. Here, we need to use a
ref.
A ref to keep the current state’s value
It’s not that usual in React to use references (or refs), but they can be handy when dealing with async code in components.
You can see a ref as a container for any value, with the ability to read and
update this value. When using hooks, you can create a ref with useRef.
First, let’s declare our ref. Its initial value will be the same as original,
and we’ll update it every time original changes:
const originalRef = useRef(original)useEffect(() => {originalRef.current = original}, [original])
You may wonder what the purpose of having a ref containing the same value as our state is. Why not use the state directly?
Each time React renders the component, it creates a new instance of the
original variable since we are in a new call of the component function.
Therefore, in a callback called asynchronously, it is impossible to say whether
the variable we see is still in the current function call.
But with the ref, it’s different: the ref is created once and never reassigned
after. This is why we write originalRef.current = original and not
originalRef = original. A ref could not be a primitive type (number, string,
boolean), it has to be a reference type (object, function, array, date, etc.) so
we can update its value (current) without reassigning the ref itself to a new
value.
To read the current value, we use the same originalRef.current, and this time
it will always contain the correct value, even in a callback called
asynchronously several seconds after initialization:
const handleClick = () => {setTimeout(() => {setCopy(originalRef.current)}, 2000)}
Here is the code of the complete solution using a ref:
Result
Something important to note: you may want to use only a ref to store the
original value here, instead of a local state. It wouldn’t work: updating a ref
value does not trigger anything in the React component's lifecycle. No
rerendering, so no updates via useEffect when watching the ref, etc.