如果useLayoutEffect内部也有状态更新,则useEffect内部的状态更新被推迟

问题描述 投票:0回答:1

在 React 18 中

如果组件中有

useLayoutEffect
useEffect
并且它们都更新组件的状态,那么
useEffect
更新将被推迟,直到
useLayoutEffect
停止触发渲染。

示例

export const App = () => {
  const [done, setDone] = useState(false);
  const [counter, setCounter] = useState(0);

  useLayoutEffect(() => {
    if (counter < 20) {
      setCounter(counter + 1);
    }
  }); // intentionally run on every render

  useEffect(() => {
    setDone(true);
  }); // intentionally run on every render

  console.log("render done", done);
  console.log("render counter", counter);
  console.log("\n");

  return <div>{counter} renders</div>;
};

给出输出

render done false
render counter 0
 
render done false
render counter 1

render done false
render counter 2

...

render done false
render counter 20

render done true
render counter 20
仅当我们停止在

done

 内触发渲染后,
useLayoutEffect
状态才会更新。

CodeSandbox:https://codesandbox.io/s/github/Andronomewalka/rec_render

GitHub 存储库:https://github.com/Andronomewalka/rec_render

在 React 17 中

提供的代码结果为

render done false
render counter 0

render done true
render counter 1
...

因此,来自

useEffect
useLayoutEffect
的状态更新应用于同一渲染。

我在 Bug 中发现了与 useSyncExternalStore 类似的问题:useEffect 中的 setState 在 React 18 #25593 线程中不可靠。

我的问题是:在React 18中是否会发生这种情况,因为

useLayoutEffect
内部的代码以及从中安排的所有状态更新都会阻止浏览器重新绘制屏幕,同时
useEffect
内部的状态更新在浏览器之后处理重新绘制屏幕?

那么这样一来,

useLayoutEffect
的更新会延迟
useEffect
的更新吗?

在 React 17 中它以另一种方式工作?

https://beta.reactjs.org/reference/react/useLayoutEffect#caveats https://beta.reactjs.org/reference/react/useEffect#caveats

reactjs react-hooks
1个回答
1
投票

tldr;

done
状态在
useLayoutEffect
完成之后才更新的原因是因为在大多数情况下
useEffect()
在屏幕渲染(绘制) 之后运行 并且
useLayoutEffect()
会阻止它。

@TusharShahi 在评论中提出了一个公平的问题 - 这篇文章 应该澄清 18 中的更改。人们可以使用

flushSync()
来强制重新渲染。请注意,出于性能原因,文档中的所有地方都建议避免使用此技术。对于简单的事情就可以了。

详情:

来自

useLayoutEffect
文档

useLayoutEffect
内的代码以及安排的所有状态更新 它阻止浏览器重新绘制屏幕。

其他说明来自该页面上的示例。

来自 useLayoutEffect 与 useEffect 示例 1:

React 保证

useLayoutEffect
内的代码以及任何状态 其中计划的更新将在浏览器之前处理 重新绘制屏幕。

这是文档所说的关于

useEffect()

如果您的 Effect 不是由交互(如点击)引起的,React 让浏览器在运行之前先绘制更新的屏幕 你的效果。如果您的效果正在做一些视觉效果(例如, 定位工具提示),并且延迟很明显(例如, 闪烁),需要将 useEffect 替换为 useLayoutEffect。

即使您的效果是由交互(例如点击)引起的, 浏览器可能会在处理状态更新之前重新绘制屏幕 在你的效果里面。通常,这就是您想要的。但是,如果您 必须阻止浏览器重新绘制屏幕,您需要替换 useEffect 和 useLayoutEffect。

© www.soinside.com 2019 - 2024. All rights reserved.