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

2024-05-15

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

我有一个setTimeout函数在一个useEffect功能。

The setTimeout超时值为50000ms.

我想要 SetTimeout 处理程序做什么

After 50000ms the setTimeout函数检查是否已收到 api 调用响应。

如果尚未收到回复,则setTimeout函数应该重新启动 api 调用,因为这样调用就会被视为超时。

回调处理程序正在做什么

After 50000ms,即使已收到响应,setTimeout 处理程序仍会发起 api 调用。

我尝试记录状态的输出,然后它返回一个缓存的状态,即使该状态已传递到useEffect功能并且应该已经更新

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

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

Logic 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])

Logic 2

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

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


状态过时的原因:

The 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>
  );
}
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"));
<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>

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

您可以使用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>
  );
}
const { useState, useEffect, useRef } = React;

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>
  );
}

ReactDOM.render(<App />, document.querySelector("#root"));
<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>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

useEffect 中的 setTimeout 函数输出缓存的状态值 的相关文章

随机推荐