返回

深入理解useEffect和useLayoutEffect: 源码解析与时机剖析

前端

useEffect和useLayoutEffect:同宗同源,各司其职

useEffect和useLayoutEffect同为React Hooks,用于处理副作用。它们都有一个回调函数作为参数,该回调函数会在组件渲染后或更新后执行。然而,它们在执行时机上略有不同。

  • useEffect会在组件渲染后立即执行,并在组件卸载前执行清理函数(如果有的话)。
  • useLayoutEffect会在浏览器完成布局后执行,并在组件卸载前执行清理函数(如果有的话)。

从源码层面比较useEffect和useLayoutEffect

// useEffect源码
export function useEffect(create, deps) {
  const nextDeps = useMemo(() => deps, deps && deps.every(isStable));
  const hasPrevDeps = nextDeps !== deps;
  const prevEffect = useUpdateEffect();
  // ...
}

// useLayoutEffect源码
export function useLayoutEffect(create, deps) {
  const nextDeps = useMemo(() => deps, deps && deps.every(isStable));
  // ...
  if (enableLayoutEffect) {
    if (!didWarnUseLayoutEffectForUI && warnAboutLayoutEffects) {
      didWarnUseLayoutEffectForUI = true;
      console.error(
        'Warning: useLayoutEffect does nothing on the server, because its effect ' +
          'cannot be inspected until after rendering. Consider server-side rendering ' +
          'or switching to useEffect.',
      );
    }
  }

  // ...
}

从源码中可以看出,useEffect和useLayoutEffect在实现上非常相似,但也有细微差别。

  • useEffect使用useUpdateEffect钩子来管理效果的创建和销毁,而useLayoutEffect使用useLayoutEffectImpl钩子。
  • useEffect在组件渲染后立即执行,而useLayoutEffect会在浏览器完成布局后执行。
  • useEffect可以在服务器端渲染中使用,而useLayoutEffect不能。

类组件生命周期与this.setState的callback的执行时机

在类组件中,有几个常用的生命周期方法,分别是:

  • componentDidMount:在组件首次挂载后调用。
  • componentDidUpdate:在组件更新后调用,但不适用于首次挂载。
  • componentWillUnmount:在组件卸载前调用。

this.setState的callback会在组件更新后调用,但不适用于首次挂载。

useEffect和useLayoutEffect与类组件生命周期的对应关系

useEffect和useLayoutEffect与类组件生命周期的对应关系如下:

React Hook 类组件生命周期 执行时机
useEffect componentDidMount, componentDidUpdate 组件渲染后立即执行
useLayoutEffect componentDidMount, componentDidUpdate 浏览器完成布局后执行

useEffect和useLayoutEffect的最佳实践

在使用useEffect和useLayoutEffect时,有一些最佳实践需要遵循:

  • 尽量使用useEffect,只有在需要在浏览器完成布局后执行副作用时才使用useLayoutEffect。
  • 在useEffect和useLayoutEffect中,尽量使用函数式更新,避免使用类组件中的this.setState。
  • 在useEffect和useLayoutEffect的清理函数中,尽量避免执行昂贵的操作,比如网络请求或DOM操作。

结语

通过本文的深入剖析,我们对useEffect和useLayoutEffect有了更深入的理解。我们了解了它们在执行时机上的细微差别,也探讨了它们与React生命周期以及this.setState回调的执行时机之间的关系。希望这些知识能够帮助你在实际开发中更加高效地使用React Hooks。