reactjs - useEffect 依赖列表及其包含所有使用变量的最佳实践
问题描述
根据https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect在使用 useEffect 依赖数组时的反应文档,您应该传入效果内使用的所有值。
如果您使用此优化,请确保数组包含组件范围内的所有值(例如 props 和 state),这些值会随时间变化并由效果使用。否则,您的代码将引用以前渲染中的陈旧值。详细了解如何处理函数以及当数组值变化太频繁时该怎么做。
我不知道钩子在幕后是如何工作的,所以我在这里猜测一下。由于闭包中的变量可能会过时,这意味着该函数被缓存在某个地方。但是为什么要缓存该函数,因为除非依赖项更改并且无论如何都需要重新创建该函数,否则它不会被调用?
我做了一个小的测试组件。
function App() {
const [a, setA] = useState(0);
const [b, setB] = useState(0);
useEffect(() => {
console.log("b", b);
}, [a]);
return (
<div>
<div>App {a}</div>
<button
onClick={() => {
setA(a + 1);
}}
>
AddA
</button>
<button
onClick={() => {
setB(b + 1);
}}
>
AddB
</button>
</div>
);
}
你可以在这里尝试一下:https ://codesandbox.io/s/react-hooks-playground-forked-m5se8它工作得很好,没有陈旧的值。有人可以解释我错过了什么吗?
编辑:在反馈我的问题不完全清楚后,添加一个更具体的问题:
当页面加载后我单击 AddB 按钮然后单击 AddA 按钮时,控制台中显示的值为 1。根据文档,我应该得到一个过时的值 (0)。为什么不是这样?
解决方案
当页面加载后我单击 AddB 按钮然后单击 AddA 按钮时,控制台中显示的值为 1。根据文档,我应该得到一个过时的值 (0)。为什么不是这样?
在这种情况下您看不到陈旧值的原因是当您单击时AddA
会发生重新渲染。在该新渲染中,由于 的值a
与以前的渲染不同,因此useEffect
将安排在 react 更新 UI 后运行(但是它将引用的值将来自当前渲染useEffect
- 因为每次传递给的函数都会重新创建)它的依赖项发生了变化,因此它从该渲染中捕获值)。
由于上述原因,这就是为什么您会看到b
.
但是如果你有这样的代码
React.useEffect(() => {
const timer = window.setInterval(() => {
setB(b + 1);
}, 1000);
return () => {
window.clearInterval(timer);
};
}, []);
b
将是一个陈旧的关闭。由于useEffect
没有重新运行,b
总是从创建它的渲染中获得价值,即第一次渲染,上面的代码不会按预期工作,你可以自己检查。
我将从上面添加 Patrick Roberts 的此评论,因为它很好地解释了恕我直言,当他们说“否则,您的代码将引用以前渲染中的陈旧值”时反应文档可能意味着什么:
问题是您必须首先单击 AddA 按钮。在您单击 AddB 和 AddA 之间,效果会从陈旧的闭包中引用 b
推荐阅读
- python-3.x - 此代码中使用的 x 的“for 循环”是否是 while x<=num 的有效替代品?
- javascript - 突变没有观察并记录到控制台
- android - Vivo 手机在膨胀布局时出现运行时错误
- android - 如何在 Flutter 中创建水平滚动的垂直列表视图?
- mysql - 用临时表更新表非常慢
- java - 如何调用返回 c++ 程序中类的对象并接收该对象的 java 函数?
- javascript - webpack build oidc-client 导致 Oidc.Usermanager 不是构造函数
- python - Python 修补类链下的类方法
- python - 如何计算python中数组元素的距离?
- qt - 添加到 QGraphicsScene 时如何更新 OpenGL 绘图(QOpenGLWidget)?