reactjs - useEffect Hook 示例:导致重新渲染的原因是什么?
问题描述
我试图弄清楚 useEffect 何时导致重新渲染。我对以下示例的结果感到非常惊讶:
https://codesandbox.io/embed/romantic-sun-j5i4m
function useCounter(arr = [1, 2, 3]) {
const [counter, setCount] = useState(0);
useEffect(() => {
for (const i of arr) {
setCount(i);
console.log(counter);
}
}, [arr]);
}
function App() {
useCounter();
console.log("render");
return <div className="App" />;
}
这个例子的结果如下:
我不知道为什么:
- 该组件仅渲染 3 次(我猜该组件会为每次调用
setCount
+ 一个初始渲染重新渲染 - 所以 4 次) - 计数器只有两个值 0 和 3:我猜,正如本文所述,每个渲染都会看到自己的状态和道具,因此整个循环将以每个状态作为常数 (1, 2, 3) 运行-->但是为什么状态永远不是 2?
解决方案
我将尽我所能解释(或介绍)正在发生的事情。我也在第 7 点和第 10 点做了两个假设。
- 应用组件挂载。
useEffect
在安装后调用。useEffect
将“保存”初始状态,因此counter
无论何时在其中引用时都将为 0。- 循环运行 3 次。每次迭代
setCount
都被调用以更新计数,并且控制台日志记录根据“存储”版本为 0 的计数器。因此,数字 0 在控制台中记录了 3 次。因为状态已经改变 (0 -> 1, 1 -> 2, 2 -> 3) React 设置像一个标志或其他东西来告诉自己记住重新渲染。 - React 在执行期间没有重新渲染任何东西
useEffect
,而是等到useEffect
完成重新渲染。 - 完成
useEffect
后,React 会记住counter
在其执行期间的状态已更改,因此它将重新渲染应用程序。 - 应用程序重新渲染并
useCounter
再次调用。注意这里没有参数被传递给useCounter
自定义钩子。 假设: 我自己也不知道这一点,但我认为默认参数似乎是再次创建的,或者至少以某种方式让 React 认为它是新的。因此,因为arr
被视为新的,useEffect
钩子将再次运行。useEffect
这是我可以解释第二次跑步的唯一原因。 - 在第二次运行期间
useEffect
,counter
的值为 3。因此,控制台日志将按预期将数字 3 记录 3 次。 - 在
useEffect
第二次运行后,React 发现计数器在执行期间发生了变化(3 -> 1, 1 -> 2, 2 -> 3),因此应用程序将重新渲染,从而导致第三个“渲染”日志。 - 假设: 因为从 App 的角度来看,钩子的内部状态
useCounter
在这次渲染和之前的渲染之间没有改变,它不会执行其中的代码,因此useEffect
不会第三次调用。所以应用程序的第一次渲染总是会运行钩子代码。第二次 App 看到 hook 的内部状态counter
从 0 变为 3 决定重新运行,第三次 App 看到内部状态是 3 仍然是 3 所以决定不重新运行-运行。这是我能想出的让钩子不再运行的最好理由。您可以在钩子本身中放置一个日志,以查看它实际上不会运行第三次。
这就是我看到的情况,我希望这让它更清楚一点。
推荐阅读
- android - 如何让 ImageView 决定自定义 Drawable 的大小?
- javascript - 我无法从 html 表中获取数据
- android - 如何在Android中添加时间
- javascript - 祖父回调的 React 组件在孙子调用后不会重新渲染页面
- r - 带有滑动窗口的动画 ggplot 时间序列图
- anaconda - rstudio网状结构故障
- python - 将权限/访问权限授予某些人
- python - 层次注意网络中的输入层代表什么
- java - spring 如何定位一个包下的所有配置类,并在spring中存储每个配置类的实例
- python - tf.Dataset.batch 默认是否预加载以及如何禁用?