Javascript 为什么AddEventListener上的回调打印旧状态?
我有一个回调函数,在其中我设置了一些状态。国家似乎发生了变化。但是,我不能在回调函数中引用更新的状态-它总是打印出初始状态 所以我的问题是为什么会发生这种情况,如果我想检查回调中的更新状态,我应该如何继续Javascript 为什么AddEventListener上的回调打印旧状态?,javascript,reactjs,Javascript,Reactjs,我有一个回调函数,在其中我设置了一些状态。国家似乎发生了变化。但是,我不能在回调函数中引用更新的状态-它总是打印出初始状态 所以我的问题是为什么会发生这种情况,如果我想检查回调中的更新状态,我应该如何继续 import React, { useState, useEffect } from "react"; import Context from "./ctx"; export default props => { const [state, setState] = useState
import React, { useState, useEffect } from "react";
import Context from "./ctx";
export default props => {
const [state, setState] = useState({
x: 0,
y: 0
});
useEffect(() => {
window.addEventListener("resize", e => {
setState({
x: e.target.window.visualViewport.width,
y: e.target.window.visualViewport.height
});
console.log(`${JSON.stringify(state)}`); <<---logs the initial state.
});
return () => {
window.removeEventListener("resize");
};
}, []);
console.log(`${JSON.stringify(state)}`); <<---logs updated versions of the state.
return (
<Context.Provider
value={{
...state
}}
>
{props.children}
</Context.Provider>
);
};
import React,{useState,useffect}来自“React”;
从“/ctx”导入上下文;
导出默认道具=>{
常量[状态,设置状态]=使用状态({
x:0,,
y:0
});
useffect(()=>{
window.addEventListener(“调整大小”,e=>{
设定状态({
x:e.target.window.visualViewport.width,
y:e.target.window.visualViewport.height
});
console.log(`${JSON.stringify(state)}`)javascript闭包中的原因。useffect
回调有一个旧版本的数据,因为deps数组为空,外部数据不会更新。因此使用了旧版本的state
对象。这就是为什么得到相同的值的原因。SetState是异步的,在执行该行代码后,您将得到旧值,o您可以做的一件事是侦听要更新的状态,然后在该效果中输出console.log,例如:
useffect(()=>{
console.log(`${JSON.stringify(state)}`);//{
console.log(`${JSON.stringify(state)}`);//{
设定状态({
x:e.target.window.visualViewport.width,
y:e.target.window.visualViewport.height
});
控制台日志(“更新”);
});
return()=>{
removeEventListener(“调整大小”,处理程序);
};
}, []);
console.log(`${JSON.stringify(state)}`);//您面临的问题与两件事有关:
React是如何工作的
闭包(创建闭包的函数+环境)
1.React是如何工作的
您已经创建并导出了功能性React组件,它接受一些道具,使用钩子进行状态管理,并呈现一些内容。
当组件(或其父组件)中的某些道具或状态更改发生反应时,将重新呈现组件,这意味着:它将直接调用您的函数,如yourComponent(props)
。
要点是:每次重新渲染时都会执行函数体,同时调用useState
和useffect
2.闭包(创建闭包的函数+环境)
每当我们在JavaScript中创建/定义某个函数时,它都与创建它的环境一起存储在运行时内存中。
在您的例子中,您在这里定义了有问题的函数(同时将其作为useffect
的回调提供):
()=>{
window.addEventListener(“调整大小”,e=>{
设定状态({
x:e.target.window.visualViewport.width,
y:e.target.window.visualViewport.height
});
console.log(`${JSON.stringify(state)}`);useState是异步函数。在更新后尝试访问它时,它不能是最新的。您可以添加一个新的useEffect回调,其状态为数组参数:useEffect(=>console.log(state),[state]))
@demkovych是正确的,因为状态更新程序是异步的,所以在下一次渲染之前,不能假定状态已经更新
useEffect(() => {
window.addEventListener("resize", e => {
setState({
x: e.target.window.visualViewport.width,
y: e.target.window.visualViewport.height
});
console.log(`${JSON.stringify(state)}`);
});
return () => {
window.removeEventListener("resize");
};
}, []); // <-- HERE
useEffect(() => {
window.addEventListener("resize", e => {
setState({
x: e.target.window.visualViewport.width,
y: e.target.window.visualViewport.height
});
console.log(`${JSON.stringify(state)}`);
});
return () => {
window.removeEventListener("resize");
};
}, [state]); // <-- NOW THE CALLBACK WILL BE EXECUTED EVERY TIME STATE CHANGES
useEffect(() => {
window.addEventListener("resize", e => {
setState({
x: e.target.window.visualViewport.width,
y: e.target.window.visualViewport.height
});
console.log(`${JSON.stringify(state)}`);
});
return () => {
window.removeEventListener("resize");
};
}); // <-- WITHOUT SECOND ARGUMENT