pin*_*o64 4 javascript reactjs
我有一个回调函数,我在其中设置了一些状态。国家似乎变异得很好。但是,我无法引用回调函数内的更新状态 - 它总是打印出初始状态。
所以我的问题是为什么会发生这种情况,如果我想检查回调内的更新状态,我应该如何进行?
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>
);
};
Run Code Online (Sandbox Code Playgroud)
小智 5
您面临的问题与两件事有关:
1. React 如何工作
您已经创建并导出了函数式 React 组件,该组件接受一些 props,使用钩子进行状态管理,并呈现一些内容。每当组件(或其父组件)中的某些 props 或状态发生变化时,react 都会重新渲染您的组件,这意味着:它实际上会调用您的函数,例如yourComponent(props). 要点是:每次重新渲染发生时,函数体都会被执行,同时还会调用useState和useEffect。
2. 闭包(函数+创建它的环境)
每当我们在 JavaScript 中创建/定义某个函数时,它都会与创建它的环境一起存储在运行时内存中。在您的情况下,您在此处定义相关函数(并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");
};
}
Run Code Online (Sandbox Code Playgroud)
它创建的环境是功能组件的主体。
因此,每当 React 调用你的组件时,就会创建新的环境,并定义新的回调函数。但是,因为您在此处提供了空数组作为第二个参数(表示依赖项数组 docs):
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
Run Code Online (Sandbox Code Playgroud)
提供的函数将仅执行一次 - 在组件安装/首次渲染时,因为您说它不依赖于任何其他值,所以不需要再次触发该效果。
底线:您在每个渲染上定义新的回调函数,但实际上只调用了第一个回调函数。当它被定义时,状态的值为{ 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)}`);
});
return () => {
window.removeEventListener("resize");
};
}, [state]); // <-- NOW THE CALLBACK WILL BE EXECUTED EVERY TIME STATE CHANGES
Run Code Online (Sandbox Code Playgroud)
或者,您可以完全省略第二个参数,这样它将在每次渲染时触发:
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
Run Code Online (Sandbox Code Playgroud)
它可能看起来很昂贵,但官方 React 文档说它不应该对事件侦听器的使用产生任何性能影响,因为 addEventListener DOM api 非常高效。但如果您发现一些性能问题,您始终可以使用第一个解决方案:)。
希望能帮助到你!