[英]React global variable not being updated inside function that passing to children component
[英]React: Function not reading updated global variable
運行按鈕已被單擊。 當我單擊跳過按鈕時,我無法到達 Switch Case 2。組件 state 跳過已更新,但 function 仍打印跳過的舊值。
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>
</>
)
}
誰能說出可能導致此問題的原因或實現此目標的正確方法?
這背后有很多因素。 因此,讓我們通過一個最小可重現的用例示例來理解並逐步破壞代碼。
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>
</>
);
當第一次調用recursiveCallback()
時,會創建一個閉包,其中foo
的值為 0。
當foo
使用setFoo(foo + 1)
遞增時,會在 memory (假設是x )中創建一個新的recursiveCallback
引用,其中包含foo
的更新值。
現在,當再次recursiveCallback()
時,它不會調用自身的x引用,而是調用它的當前引用,這將具有foo
的值存在於詞法環境中,即 0。所以,它出現了正如您在<p>{foo}</p>
中看到的那樣, foo
並沒有遞增,但實際上它是遞增的。
因此,修復上述代碼段的解決方案是在 useEffect 中調用nonRecursiveCallback
,每次更新foo
的值時,它總是會調用useEffect
的新引用!
// 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]);
除非不滿足終止條件,否則您可以在useEffect
鈎子的幫助下重復調用它,而不是遞歸調用run()
。
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>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.