Reputation: 8420
I have a React table component where both Column
component and a button component rendered inside a column, triggers some function. Example of the table:
<Table>
<Column onCellClick={handleCell}>
<button onClick={handleButton} />
</Column>
</Table>
Those 2 handle functions are called at the same click, and they trigger some useStates
:
const [cell, setCell] = useState(false);
const [buttonValue, setButtonValue] = useState(false);
const handleCell = (value) => setCell(value)
const handleButton = (value) => setButtonValue(value)
SO I have a useEffect
that must trigger some code ONLY when both cell
and buttonValue
are updates. Currently I have something like:
useEffect(() => {
if (cell && buttonValue) {
// some code
setAnotherState(etc)
}
}, [cell, buttonValue]);
My problem is if I click very quickly or in random scenarios, the setAnotherState
is called before the buttonValue
or cell
are actually updated. With that if
inside the useEffect, it will work correctly only the very first time that I actually update both values, because they both are initialized as false
, but then, with many clicks, there are some outdated set states.
Any hint? how could I ensure that both states actually update before executing the code inside the if
?
I can't remove any of that 2 onClick or onCellClick, they both have different and specific values for their components, so I need them both.
Upvotes: 2
Views: 1533
Reputation: 1624
You could do something like this:
const [cell, setCell] = useState(null);
// it stores the updated status for cell
const [cellStatus, setCellStatus] = useState(false);
const [buttonValue, setButtonValue] = useState(null);
// it stores the updated status for buttonValue
const [buttonValueStatus, setButtonValueStatus] = useState(false);
const handleCell = (value) => {
setCell(value)
setCellStatus(true) // it carried a updated event
}
const handleButton = (value) => {
setButtonValue(value)
setButtonValueStatus(true) // it carried a updated event
}
useEffect(() => {
// !!(null) === false
if (!!cellStatus && !!buttonValueStatus) {
// some code
setAnotherState(etc)
// here you reset their status since they were already updated and used
// now they are not new values but old ones
setCellStatus(false)
setButtonValueStatus(false)
}
}, [cell, buttonValue]);
Thus, the basic idea is to keep track of the values in one variable and the update status in another one.
Upvotes: 1
Reputation: 16325
Here's an idea using useRef
to store the collective state as an integer value you can test against.
Cell adds 2 to the state (first taking a modulus of 2 to remove existing 2 values), Button adds 1 (first doing a bit-shift and x2 to eliminate the first bit). When the stateRef is 3, the effect knows both have been changed, sets the other state, and resets the stateRef to 0.
const [cell, setCell] = useState(false);
const [buttonValue, setButtonValue] = useState(false);
const stateRef = useRef(0);
const handleCell = (value) => {
setCell(value);
stateRef.current = stateRef.current % 2 + 2;
};
const handleButton = (value) => {
setButtonValue(value);
stateRef.current = (stateRef.current >> 1) * 2 + 1;
};
Then
useEffect(() => {
if (stateRef.current == 3) {
// some code
setAnotherState(etc)
stateRef.current = 0;
}
}, [stateRef.current]);
Upvotes: 2