React - useState - 为什么 setTimeout 函数没有最新的状态值?
React - useState - why setTimeout function does not have latest state value?
最近我在研究 React Hooks,但遇到了一个 problem/doubt?
下面是重现问题的基本实现,这里我只是在单击按钮时切换 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
这归结为闭包在 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>
给setTimeout
的函数将从_onClick
函数中获取flag
变量。 _onClick
函数在每次渲染时创建,"stores" 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
最近我在研究 React Hooks,但遇到了一个 problem/doubt?
下面是重现问题的基本实现,这里我只是在单击按钮时切换 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
这归结为闭包在 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>
给setTimeout
的函数将从_onClick
函数中获取flag
变量。 _onClick
函数在每次渲染时创建,"stores" 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