useEffect 中的 setTimeout 函数输出缓存的状态值

Fre*_*eon 10 settimeout reactjs redux react-redux

这很简单。我正在使用 Redux 来管理我的状态

setTimeout我在函数中有一个函数useEffect

setTimeout超时值为50000ms。

我想要 SetTimeout 处理程序做什么

50000mssetTimeout函数检查是否已收到 api 调用响应后。

如果尚未收到响应,setTimeout函数应重新启动 api 调用,因为调用将被视为超时。

回调处理程序正在做什么

之后50000ms,即使已收到响应,setTimeout 处理程序仍会重新启动 api 调用。

我尝试记录状态的输出,然后它返回缓存的状态,即使状态已传递到函数的依赖项数组useEffect并且应该已更新

进行 api 调用后,testDetails.isUpdatingTestDetails状态设置为false

我尝试了几种逻辑,但没有一个有效

逻辑1

 useEffect(() => {
         //Notice how i check if the testDetails is being updated before initiating the setTimeout callback
        if (testDetails.isUpdatingTestDetails === true) {
         
            setTimeout(() => {
// Inside the settimeout function the same check is also done.
// even though before 50 seconds the response is being received , the function logs the text simulating the reinitiation of an api call
                return testDetails.isUpdatingTestDetails === true &&
                    console.log("After 50 Seconds You Need To Refetch This Data")
            }, 50000);
        }
 

    }, [testDetails.isUpdatingTestDetails, testDetails])
Run Code Online (Sandbox Code Playgroud)

逻辑2

     useEffect(() => {
         setTimeout(() => {
           return testDetails.isUpdatingTestDetails === true &&
             console.log("After 50 Seconds You Need To Refetch This Data")
            }, 50000);
    }, [testDetails.isUpdatingTestDetails, testDetails])

Run Code Online (Sandbox Code Playgroud)

我上面应用的逻辑都不起作用。

Ram*_*ddy 21

状态过时的原因:

useEffect回调形成了当时状态的闭包。因此,当执行超时回调时,即使状态同时更新,它也只能使用旧状态。

一旦状态发生变化,useEffect将再次运行(因为状态是依赖项)并启动新的超时。

第二次超时将使用新状态,因为闭包是用新状态形成的。如果状态第三次更改,此超时也容易受到陈旧状态问题的影响。


解决方案:

当状态改变时,你可以清除之前的超时时间。这样,除非是最新的,否则不会执行超时回调。

export default function App() {
  const [state, setState] = useState(true);

  useEffect(() => {
    const timeout = setTimeout(() => {
      console.log(state);
    }, 5000);

    return () => {
      // clears timeout before running the new effect
      clearTimeout(timeout);
    };
  }, [state]);

  return (
    <div className="App">
      <h1>State: {state.toString()}</h1>
      <button onClick={() => setState(false)}>update</button>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

编辑 fast-leaf-zk3yo

export default function App() {
  const [state, setState] = useState(true);

  useEffect(() => {
    const timeout = setTimeout(() => {
      console.log(state);
    }, 5000);

    return () => {
      // clears timeout before running the new effect
      clearTimeout(timeout);
    };
  }, [state]);

  return (
    <div className="App">
      <h1>State: {state.toString()}</h1>
      <button onClick={() => setState(false)}>update</button>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)
const { useState, useEffect } = React;

function App() {
  const [state, setState] = useState(true);

  useEffect(() => {
    const timeout = setTimeout(() => {
      console.log(state);
    }, 5000);

    return () => {
      // clears timeout before running the new effect
      clearTimeout(timeout);
    };
  }, [state]);

  return (
    <div className="App">
      <h1>State: {state.toString()}</h1>
      <button onClick={() => setState(false)}>update</button>
    </div>
  );
}

ReactDOM.render(<App />, document.querySelector("#root"));
Run Code Online (Sandbox Code Playgroud)


如果您想在不增加延迟的情况下运行超时,则可以使用替代解决方案。如果延迟后状态发生变化,则不会启动新的超时。

您可以使用useRef钩子随时获取最新状态的引用。

这是一个例子。您可以修改以下内容以使用您的变量和逻辑。

export default function App() {
  const [state, setState] = useState(true);
  const stateRef = useRef(state);

  // this effect doesn't need any dependencies
  useEffect(() => {
    const timeout = setTimeout(() => {
      // use `stateRef.current` to read the latest state instead of `state`
      console.log(stateRef.current);
    }, 5000);

    return () => {
      // just to clear the timeout when component unmounts
      clearTimeout(timeout);
    };
  }, []);

  // this effect updates the ref when state changes
  useEffect(() => {
    stateRef.current = state;
  }, [state]);

  return (
    <div className="App">
      <h1>State: {state.toString()}</h1>
      <button onClick={() => setState(false)}>update</button>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

编辑迷人的sinoussi-tmeo2

<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)
export default function App() {
  const [state, setState] = useState(true);
  const stateRef = useRef(state);

  // this effect doesn't need any dependencies
  useEffect(() => {
    const timeout = setTimeout(() => {
      // use `stateRef.current` to read the latest state instead of `state`
      console.log(stateRef.current);
    }, 5000);

    return () => {
      // just to clear the timeout when component unmounts
      clearTimeout(timeout);
    };
  }, []);

  // this effect updates the ref when state changes
  useEffect(() => {
    stateRef.current = state;
  }, [state]);

  return (
    <div className="App">
      <h1>State: {state.toString()}</h1>
      <button onClick={() => setState(false)}>update</button>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)