简体   繁体   English

反应:Function 未读取更新的全局变量

[英]React: Function not reading updated global variable

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.当我单击跳过按钮时,我无法到达 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>
     </>
    )
}

Can anybody tell what might be causing this or a correct way to achieve this?谁能说出可能导致此问题的原因或实现此目标的正确方法?

Why recursion is not working?为什么递归不起作用?

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.当第一次调用recursiveCallback()时,会创建一个闭包,其中foo的值为 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.foo使用setFoo(foo + 1)递增时,会在 memory (假设是x )中创建一个新的recursiveCallback引用,其中包含foo的更新值。

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> .现在,当再次recursiveCallback()时,它不会调用自身的x引用,而是调用它的当前引用,这将具有foo的值存在于词法环境中,即 0。所以,它出现了正如您在<p>{foo}</p>中看到的那样, foo并没有递增,但实际上它是递增的。

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!因此,修复上述代码段的解决方案是在 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]);

Solution解决方案

Instead of calling run() recursively , you can have it called repetitively with the help of useEffect hook unless the termination condition is not satisfied.除非不满足终止条件,否则您可以在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