簡體   English   中英

反應:Function 未讀取更新的全局變量

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM