useEffect和useLayoutEffect的指南

71 阅读2分钟

这两者都可以用来做基本相同的事情,但它们的使用情况略有不同。所以这里有一些规则供你在决定使用哪个React Hook时考虑。

99%的情况下,这是你想使用的。当钩子稳定后,如果你重构你的任何类组件以使用钩子,你可能会将任何代码从componentDidMountcomponentDidUpdatecomponentWillUnmount 移到useEffect

有一个问题是,这是react渲染你的组件运行的,并确保你的效果回调不会阻止浏览器的绘制。这与类组件中的行为不同,componentDidMountcomponentDidUpdate 在渲染后同步运行。这种方式更有表现力,而且大多数情况下这是你想要的。

然而,如果你的效果是突变DOM(通过DOM节点引用),那么 DOM突变会在DOM节点被渲染和你的效果突变之间改变它的外观,那么你就不要使用useEffect 。你应该使用useLayoutEffect 。否则,当你的DOM突变生效时,用户可能会看到一个闪烁的现象。这几乎是你唯一想避免使用useEffect ,而使用useLayoutEffect 的时候。

这在React执行完所有DOM突变后立即同步运行。如果你需要进行DOM测量(比如获取元素的滚动位置或其他样式),然后进行DOM突变通过更新状态来触发同步重渲染,这就很有用。

就调度而言,其工作方式与componentDidMountcomponentDidUpdate 相同。你的代码在DOM被更新后立即运行,但在浏览器有机会 "绘制 "这些变化之前(用户在浏览器重新绘制后才真正看到更新):

  • useLayoutEffect:如果你需要突变DOM和/或确实需要进行测量的话
  • useEffect:如果你根本不需要与DOM交互,或者你的DOM变化是不可观察的(说真的,大多数时候你都应该使用这个)。

还有一种情况,你可能想用useLayoutEffect ,而不是useEffect ,那就是你正在更新一个值(比如ref ),并且你想在其他代码运行之前确保它是最新的。比如说:

const ref = React.useRef()
React.useEffect(() => {
  ref.current = 'some value'
})

// then, later in another hook or something
React.useLayoutEffect(() => {
  console.log(ref.current) // <-- this logs an old value because this runs first!
})

所以在这样的情况下,解决方案是useLayoutEffect

这都是关于默认的。默认行为是在React运行你的代码之前,让浏览器根据DOM的更新来重新绘制。这意味着你的代码不会阻塞浏览器,而用户会更快看到DOM的更新。所以在大多数情况下,坚持使用useEffect