So let's say I have this code:
const [test, setTest] = useState();
const [test2, setTest2] = useState();
useEffect(() => {
setTest2(undefined);
}, [test]);
const calledFunction => () {
setTest(whatever);
setTest2(thisIsWhatIwant);
}
return (
<>
{test2}
<button onClick={() => calledFunction}></button>
</>
);
After all of this code, I will get undefined in test2 even if I want to have "thisIsWhatIwant". So there is a small hack to achieve this:
const [test, setTest] = useState();
const [test2, setTest2] = useState();
useEffect(() => {
setTest2(undefined);
}, [test]);
const calledFunction = () => {
setTest(whatever);
setTimeout(() => setTest2(thisIsWhatIwant), 1);
}
This will work because setTimeout
will push setTest2 to the end of the stack (after the useEffect
).
Is this bad practice? If so, is there any way to achieve what I want in a cleaner way?
Thanks.
//edit: calledFunction is called on click, on another button
CodePudding user response:
The ambiguity of this question makes this hard to answer. I'll assume the following scenario:
You want to reset
test2
iftest
is changed except in some special circumstance.
You could create a custom setter instead of using a useEffect
callback.
const [test, _setTest] = useState();
const [test2, setTest2] = useState();
const setTest = useCallback((newTest) => {
_setTest(newTest);
setTest2(undefined);
}, []);
const calledFunction = () => {
_setTest(whatever);
setTest2(thisIsWhatIwant);
}
return (
<>
{test2}
<button onClick={() => calledFunction}></button>
</>
);
In the above code we've defined the setter setTest
to update both test
and reset test2
. "normal" code will be calling the setTest
function, which closely follows your previous behaviour.
However in your special circumstance you can use _setTest
which only updates the test
without resetting test2
.
Note there are two important differences:
- The custom setter is not called on component mount.
- If the value passed to
setTest
has the same identity as the previous value, then theuseState
callback will not be called andtest2
is never reset. Whereas with this custom settertest2
will be reset regardless.
These differences in behaviour might or might not be relevant depending on the context.
CodePudding user response:
Another way to do this could be using a ref
which tells you that you want to skip your code in this effect.
const [test, setTest] = useState();
const [test2, setTest2] = useState();
const skipSetter = useRef(false);
useEffect(() => {
if(skipSetter.current){
setTest2(undefined);
}
skipSetter.current = false;
}, [test]);
const calledFunction => () {
skipSetter.current = true;
setTest(whatever);
setTest2(thisIsWhatIwant);
}
The primary difference between this and your current approach is that there won't be an extra rerender, which happens in your implementation because of a setState
inside the setTimeout
callback.
Note: You will have to be careful with the value of ref variable though and ensure you are setting it correctly where applicable.