I have a parent component (Parent). This component (hook) has a state variable called refs. This variable is an object, which saves all refs used in Parent component and all Child components like this way:
const [refs, setRefs] = useState({
"ref1": ref1,
"ref2": ref2,
"ref3": ref3,
});
Please don’t ask why. Actually this is needed in the current solution.
So the code of the Parent component is like this:
export default function Parent(props) {
const ref1 = useRef(null);
const ref2 = useRef(null);
const ref3 = useRef(null);
const [refs, setRefs] = useState({
"ref1": ref1,
"ref2": ref2,
"ref3": ref3,
});
useEffect(() => {
updateRefs({
"ref1": ref1,
"ref2": ref2,
"ref3": ref3,
});
},[
ref1,
ref2,
ref3
])
function updateRefs(updateRefs) {
let updatedRefs = Object.assign({}, refs, updateRefs);
setRefs(updatedRefs);
}
return (
<div>
<Child1
updateRefs={updateRefs}
/>
<Child2
updateRefs={updateRefs}
/>
</div>
)
}
In this example the Parent component has 3 refs. These are saved directly during the initialization of the variable refs as shown above. When they are assigned to a DOM element, the useEffect is being called to update them.
Now to my issue.
Now the Childs are returning their refs by calling the function updateRefs.
This becomes a problem, when I have more than 1 Child in my Parent component.
Let’s say Child1 is returning ref4 and ref5, while Child2 is returning ref6 and ref7.
When the Parent component is rendering, Child1 is returning its refs. So before the update of Child1, the state of refs is still containing ref1, ref2 and ref3. After the update, it should also contain ref4 and ref5.
Now when Child2 is returning its refs, the state is still unchanged despite the update of Child1 (= ref1, ref2 and ref3).
So Child2 is now calling updateRefs, but without the previous update of Child1.
In the end, the state will be
ref1: ref1,
ref2: ref2,
ref3: ref3,
ref6: ref6,
ref7: ref7
instead of having all Child updateRefs:
ref1: ref1,
ref2: ref2,
ref3: ref3,
ref4: ref4,
ref5: ref5,
ref6: ref6,
ref7: ref7
Is there a way to "wait" for the updated state from Child1 and then do the updates of Child2?
>Solution :
This relies on the state of refs at the time this function was defined:
let updatedRefs = Object.assign({}, refs, updateRefs);
setRefs(updatedRefs);
So if both child components invoke this same instance of updateRefs then they’re both using the same instance of refs. This creates a race condition and whoever submits their update last wins.
State updates are queued/batched, and you can use the state setter callback to make use of the state value at that point in the queue/batch rather than the state value that was available when this function was defined.
For example:
setRefs(prev => {
let updatedRefs = Object.assign({}, prev, updateRefs);
return updatedRefs;
});
That way the second queued/batched state update (the previous "winner" of the race condition) would be using the value prev, which would include changes from the first queued/batched state update as well.