React v16 和 v18 之间的 useEffect 回调执行对比,预期行为是什么?

cla*_*ght 6 javascript reactjs react-hooks

最近我升级到了 React 18,从那以后我开始发现一个潜在的问题。请查看以下链接,我在两个不同版本中实现了相同的应用程序。

React 16: https: //codesandbox.io/s/react-16-8-0-forked-5pyqvg ?file=/src/index.js

React 18:https://codesandbox.io/s/Friendly-moore-9yb0xk? file=/src/index.js

正如您所看到的,我将一个点击侦听器附加到文档中。在 React 16 版本中,当我们单击时,正如预期的那样,它会记录以前的状态值,但在 React 18 中,React 能够以某种方式识别更新的状态值并记录当前值。两者中哪一个应该是预期的行为以及它如何比其他方式更好?

Tus*_*ahi 3

我无法评论预期的行为是什么。我假设新版本中任何包的行为都是预期的。

对我个人而言,React 18 中的行为更有意义。我认为我不想用较旧的值运行我的侦听器。但这是我的意见。

在阅读答案的其余部分之前要记住的要点:

1. React 在下次运行效果之前会清除先前渲染的效果。

2. 离散输入是一种事件类型,其中一个事件的结果可以影响下一个事件的行为,例如单击或按下。

您看到此行为的主要原因是因为useEffect回调在 React 18 中的事件侦听器之前运行。因此,在第一次useEffect运行的事件侦听器之前,它会因清理而被删除,并附加一个新的事件侦听器。该侦听器具有更新的(正确的)状态值。

查看这两个沙箱中的日志:linklink

在 React 18 中,useEffect 的回调在 Document 事件监听器本身之前运行(但在 React 16 中则不然)。在 React 18 中,旧值的闭包不存在。

React 16 中的顺序是:

  1. 按钮点击事件
  2. 重新渲染(由于状态更新)
  3. 事件监听器代码
  4. 清理(​​来自之前的 useEffect)
  5. 新的 useEffect (附加新的事件监听器)

与 React 18 进行对比:

  1. 按钮点击事件
  2. 重新渲染(由于状态更新)
  3. 清理(​​来自之前的 useEffect)
  4. 新的 useEffect (附加新的事件监听器)
  5. 新的事件监听器代码

这是一个讨论 线程中提到的例子就是一个计数器。

总而言之:

在 React 18 中,当 useEffect 是离散输入的结果时,它会同步触发。例如,如果 useEffect 附加一个事件侦听器,则保证在下一个输入之前添加该侦听器。

线程中的一些评论:

在 React 17 及更低版本中,传递给的函数useEffect通常在浏览器绘制后异步触发。我们的想法是将尽可能多的工作推迟到绘制,这样用户体验就不会被延迟。然而,虽然被动效果不会阻止浏览器绘画,但它们需要在下一个离散输入事件之前触发,以便事件系统可以观察到结果。(就像在这种情况下我们需要记录正确的计数值)

注意:上述行为仍然不同,useLayoutEffect因为它不会阻塞主线程(这会阻塞渲染)。