反应-useState-为什么setTimeout函数没有最新状态值?

时间:2019-03-16 15:37:14

标签: javascript reactjs react-hooks

最近我正在研究React Hooks,并陷入一个问题/疑问?

下面是重现该问题的基本实现,在这里,我只是单击按钮即可切换flag(状态)变量。

  const [flag, toggleFlag] = useState(false);
  const data = useRef(null);
  data.current = flag;

  const _onClick = () => {
    toggleFlag(!flag);
    // toggleFlag(!data.current); // working

    setTimeout(() => {
      toggleFlag(!flag); // does not have latest value, why ?
      // toggleFlag(!data.current); // working
    }, 2000);
  };

  return (
    <div className="App">
      <button onClick={_onClick}>{flag ? "true" : "false"}</button>
    </div>
  );

我想出了其他方法来克服此问题,例如使用useRef或useReducer,但这是正确的吗?或者是否有其他方法只能通过useState解决此问题?

此外,如果有人解释为什么我们会在setTimeout中获得旧的状态值,这将非常有帮助

沙盒URL-https://codesandbox.io/s/xp540ynomo

2 个答案:

答案 0 :(得分:2)

这归结为闭包在JavaScript中的工作方式。给setTimeout的函数将从初始渲染中获取flag变量,因为flag没有被突变。

您可以给toggleFlag提供一个函数作为参数。此函数将获取正确的flag值作为参数,并且此函数返回的值将替换状态。

示例

const { useState } = React;

function App() {
  const [flag, toggleFlag] = useState(false);

  const _onClick = () => {
    toggleFlag(!flag);

    setTimeout(() => {
      toggleFlag(flag => !flag)
    }, 2000);
  };

  return (
    <div className="App">
      <button onClick={_onClick}>{flag ? "true" : "false"}</button>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

答案 1 :(得分:0)

赋予setTimeout的函数将从flag函数获得_onClick变量。 _onClick函数在每个渲染器中创建,并“存储” flag变量在此渲染器中获取的值。

function App() {
  const [flag, toggleFlag] = useState(false);
  console.log("App thinks that flag is", flag);

  const _onClick = () => {
    console.log("_onClick thinks that flag is", flag);
    toggleFlag(!flag);

    setTimeout(() => {
      console.log("setTimeout thinks that flag is", flag);
    }, 100);
  };

  return (
    <div className="App">
      <button onClick={_onClick}>{flag ? "true" : "false"}</button>
    </div>
  );
}

控制台:

App thinks that flag is false

_onClick thinks that flag is false
App thinks that flag is true
setTimeout thinks that flag is false

_onClick thinks that flag is true
App thinks that flag is false
setTimeout thinks that flag is true