有两个 React 钩子 useEffect
和 useLayoutEffect
,它们看起来几乎一样。
它们的调用方式甚至都相同。
useEffect(() => {
// do side effects
return () => {} /* cleanup */
}, [dependency, array]);
useLayoutEffect(() => {
// do side effects
return () => {} /* cleanup */
}, [dependency, array]);
但是它们并不完全相同,请继续往下读,了解它们不同的原因以及何时使用它们。
(长话短说:大多数时候请使用普通的 useEffect
)
useEffect 和 useLayoutEffect 之间的区别
一切都在时机上。
useEffec
异步运行,并且在将渲染绘制到屏幕上之后。
所以看起来像:
- 您以某种方式触发渲染(更改状态或父级重新渲染)
- React 渲染您的组件
- 屏幕以视觉方式更新
- 然后
useEffect
运行
useLayoutEffect
在渲染之后但屏幕更新之前同步运行。步骤如下:
- 您以某种方式触发渲染(更改状态或父级重新渲染)
- React 渲染您的组件
useLayoutEffect
运行,React 会等待它执行完成(阻塞 UI)- 屏幕以视觉方式更新
99% 的情况使用 useEffect
在大多数情况下,effect 函数会同步一些 state 或 props,它并不需要立即执行,或者直接影响页面。
就像您要拉取数据一样,这不会导致立即变更页面。
或者,如果您要设置事件处理程序。
或者,如果您要在对话框出现或消失时重置某些状态。
大多数时候,使用 useEffect
才是正确的方式。
何时使用 LayoutEffect
什么时候使用 useLayoutEffect
代替 useEffect
呢?看到它的名字你就能理解到了 :)
如果您的组件在状态更新时闪烁(例如:它首先展示为部分初始状态,然后立即重新渲染为最终状态),则可以使用 useLayoutEffect
。
这是一个(人为的)演示,借助此例子您可以理解我想表达的意思。
当您单击页面* 时,状态立即更改(value重置为 0),这将重新渲染该组件:效果是将 value 设置为某个随机数,然后再次重新渲染。
import React, {
useState,
useLayoutEffect
} from 'react';
import ReactDOM from 'react-dom';
const BlinkyRender = () => {
const [value, setValue] = useState(0);
useLayoutEffect(() => {
if (value === 0) {
setValue(10 + Math.random() * 200);
}
}, [value]);
console.log('render', value);
return (
<div onClick={() => setValue(0)}>
value: {value}
</div>
);
};
ReactDOM.render(
<BlinkyRender />,
document.querySelector('#root')
);
* 顺便提一下:在 div 上绑定 onClick 事件不利于可访问性,这里只是演示。
尝试在 CodeSandbox 上在线运行 Demo:
- useLayoutEffect
使用 useLayoutEffect (15 帧每秒)
- 使用 useEffect (50 帧每秒)
请注意,即使组件渲染了两次,该版本的 useLayoutEffect
外观也只有视觉更新一次。 useEffect
另一方面,该版本在视觉上呈现两次,因此您会看到闪烁,其中的值是短暂的 0。
我应该使用 useEffect 还是 useLayoutEffect?
大多数时候, useEffect
是正确的选择。如果您的代码导致闪烁,请切换至 useLayoutEffect
并查看是否有帮助。
因为 useLayoutEffect
是同步(也称为阻塞),所以该应用在效果完成运行之前不会进行视觉更新…… 如果效果中的代码很慢,则可能会导致性能问题。再加上大多数效果在运行时不需要世界暂停。