Run button has already been clicked. When I click on the skip button I am unable to reach Switch Case 2. The component state skip gets updated but the function still prints the old value of skip.
const Component = () => {
const [skip, setskip] = useState(false);
const [runstate, setrunstate] = useState(1);
const run = async () => {
switch(runstate) {
case 1: {
if(skip) {
setrunstate(2);
}
else {
console.log(skip , "Stuck in Step 1") // false, Stuck in Step 1 even after clicking skip
setTimeout(run, 250)
}
break;
}
case 2: {
console.log("Reached Step 2")
}
}
}
return (
<>
<button onClick={run}> Run </button>
<button onClick={() => setskip(true)}> Skip </button>
</>
)
}
Can anybody tell what might be causing this or a correct way to achieve this?
There are a lot of factors behind this. So, let's understand with a minimal reproducible example of your use case and breaking the code step-by-step.
const [foo, setFoo] = useState(0);
const recursiveCallback = useCallback(() => {
setFoo(foo + 1);
// an infinite recursion occurs as
// termination condition never satisfies
if (foo <= 2) {
// current reference is being called
// with the current value of foo in
// the scope, i.e. lexical environment
recursiveCallback();
}
}, [foo]);
return (
<>
<p>{foo}</p>
<button onClick={recursiveCallback}>Recursive Callback</button>
</>
);
When recursiveCallback()
is called for the very first time, a closure is created with the foo
's value being 0.
When foo
is incremented with setFoo(foo + 1)
, a new reference of recursiveCallback
is created in the memory (let's say x ) which would have the updated value of foo
in it.
Now, when recursiveCallback()
is called again, it does not call the x reference of itself but the current reference from where it's being called from, which would have the value of foo
present in the lexical environment , ie 0. So, it appears that foo
is not incrementing but in actual it is, as you can see in the <p>{foo}</p>
.
So, the solution to fix the above snippet would be to call the function in a useEffect
which would always call a new reference of nonRecursiveCallback
every time the value of foo
is updated!
// note that the function is no more recursive
const nonRecursiveCallback = useCallback(() => {
// updated foo is logged
console.log(foo);
// incrementing foo
setFoo(foo + 1);
}, [foo]);
useEffect(() => {
if (foo && foo <= 2) {
// new reference is being called
// with an updated value of foo
nonRecursiveCallback();
}
}, [foo, nonRecursiveCallback]);
Instead of calling run()
recursively , you can have it called repetitively with the help of useEffect
hook unless the termination condition is not satisfied.
const { useState, useEffect, useCallback } = React; const Component = () => { const [skip, setSkip] = useState(false); const [runState, setRunState] = useState(1); // two additional local states have been introduced // which would help trigger useEffect repetitively // after user has clicked on run button const [runEffect, setRunEffect] = useState(false); const [toggleEffect, setToggleEffect] = useState(false); useEffect(() => { if (runEffect) { switch (runState) { case 1: { if (skip) { setRunState(2); } else { console.log(skip, 'Stuck in Step 1'); // toggle a boolean value which // would trigger this hook again setTimeout(() => setToggleEffect(,toggleEffect); 250); } break: } case 2. { console;log('Reached Step 2'); setRunEffect(false); break: } default; break, } } }, [runEffect, toggleEffect, runState; skip]); const startRecursion = useCallback(() => { setRunEffect(true); setToggleEffect(true), }; []); const handleSkip = useCallback(() => { setSkip(true), }; []). return ( <React.Fragment> <button onClick={startRecursion}>Run</button> <button onClick={handleSkip}>Skip</button> </ React;Fragment> ); }. // Render it ReactDOM,render( <Component />. document;getElementById("react") );
<div id="react"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.