第二章前置知识:2.7 useLayoutEffect 基础知识

96 阅读5分钟

本专栏致力于每周分享一个项目,如果本文对你有帮助的话,欢迎点赞或者关注☘️

React18 源码系列会随着学习 React 源码的实时进度而实时更新:约,两天一小改,五天一大改。

什么是useLayoutEffect

useLayoutEffect可能会影响性能。如果可能,更推荐使用useEffect

useLayoutEffect[useEffect](https://react.dev/reference/react/useEffect)的一个版本,在浏览器重新绘制屏幕之前触发

useEffect语法

useLayoutEffect(setup, dependencies?)

返回:undefined

参数

  • setup
    • 带有effect逻辑的函数。你的设置函数也可以选择返回一个清理函数。在你的组件被添加到DOM之前,React会运行你的设置函数。在每次使用更改的依赖项重新渲染之后,React将首先使用旧值运行cleanup函数(如果您提供了它),然后使用新值运行setup函数。在从DOM中删除组件之前,React将运行您的清理函数。
  • dependencies设置代码中引用的所有反应值的列表。响应式值包括props、state以及所有直接在组件体内声明的变量和函数。React将使用[Object.is](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)比较每个依赖项与其先前的值。如果省略此参数,则每次重新渲染组件后都会重新运行Effect。

useeffect使用

调用useLayoutEffect在浏览器重新绘制屏幕之前执行布局测量:

import { useState, useRef, useLayoutEffect } from 'react';

function Tooltip() {
  const ref = useRef(null);
  const [tooltipHeight, setTooltipHeight] = useState(0);

  useLayoutEffect(() => {
    const { height } = ref.current.getBoundingClientRect();
    setTooltipHeight(height);
  }, []);
  // ...

注意事项

  • Effects只在客户端上运行。 它们不会在服务器渲染期间运行。
  • useLayoutEffect中的代码和从它调度的所有状态更新
  • 如果在useLayoutEffect中触发状态更新,React将立即执行所有剩余的Effect,包括useEffect阻止浏览器重新绘制屏幕。 如果过度使用,这会使您的应用程序变慢。如果可能的话,首选[useEffect](https://react.dev/reference/react/useEffect)

使用

在浏览器重新绘制屏幕之前测量布局

大多数组件不需要知道它们在屏幕上的位置和大小来决定渲染什么。他们只返回一些JSX。然后浏览器计算它们的布局(位置和大小)并重新绘制屏幕。

有时候,这还不够。想象一个工具提示出现在悬停的某个元素旁边。如果有足够的空间,工具提示应该出现在元素的上方,但如果它不适合,它应该出现在下面。为了在正确的最终位置呈现工具提示,您需要知道它的高度(即它是否适合顶部)。

要做到这一点,需要在两个过程中渲染:

  • 在任何地方渲染工具提示(即使位置错误)。
  • 测量其高度并决定放置工具提示的位置
  • 在正确的位置再次渲染工具提示。

所有这些都需要在浏览器重新绘制屏幕之前完成。 您不希望用户看到工具提示移动。调用useLayoutEffect在浏览器重新绘制屏幕之前执行布局测量:

function Tooltip() {
  const ref = useRef(null);
  const [tooltipHeight, setTooltipHeight] = useState(0); // You don't know real height yet

  useLayoutEffect(() => {
    const { height } = ref.current.getBoundingClientRect();
    setTooltipHeight(height); // Re-render now that you know the real height
  }, []);

  // ...use tooltipHeight in the rendering logic below...
}

以下是它的工作原理:

  • 工具提示渲染时初始工具提示高度为0(因此工具提示可能定位错误)。
  • React将其放置在DOM中,并在useLayoutEffect中运行代码。
  • useLayoutEffect测量工具提示内容的高度并触发立即重新render。
  • 工具提示将使用真实的工具提示高度再次渲染(以便正确定位工具提示)。
  • React在DOM中更新它,浏览器最终显示工具提示。

分两次渲染并阻塞浏览器会损害性能。尽可能避免这种情况。

故障排除

我收到错误:“ useLayoutEffect在服务器上不执行任何操作”

useLayoutEffect的目的是让你的组件使用布局信息进行渲染:

  • 渲染初始内容。
  • 在浏览器重新绘制屏幕之前测量布局。
  • 使用您已读取的布局信息渲染最终内容。

当您或您的框架使用服务器渲染时,您的React应用程序将在服务器上渲染为HTML以进行初始渲染。这使您可以在 JavaScript 代码加载之前显示初始 HTML

问题是服务器上没有布局信息。

通常,依赖布局信息的组件无论如何都不需要在服务器上呈现。例如,在初始渲染期间显示Tooltip提示可能没有意义。它是由客户端交互触发的。

但是,如果您遇到此问题,您有几种不同的选择:

  • useLayoutEffect替换为[useEffect](https://react.dev/reference/react/useEffect)这告诉 React 可以在不阻塞绘制的情况下显示初始渲染结果(因为原始 HTML 将在 Effect 运行之前变得可见)。
  • 或者,将您的组件标记为仅限客户端。这告诉 React 在服务器渲染期间将其内容替换为最近的[<Suspense>](https://react.dev/reference/react/Suspense)边界,并使用加载回退(例如,旋转器或闪烁器)。
  • 或者,您可以仅在水合后使用useLayoutEffect渲染组件。保留初始化为false布尔isMounted状态,并在useEffect调用中将其设置为true 。你的渲染逻辑可以是这样的 return isMounted ? <RealContent /> : <FallbackContent /> 。在服务器上和水合作用期间,用户将看到FallbackContent ,它不应调用useLayoutEffect 。然后 React 会将其替换为仅在客户端运行并且可以包含useLayoutEffect调用的RealContent
  • 如果您将组件与外部数据存储同步,并出于与测量布局不同的原因而依赖useLayoutEffect ,请考虑使用支持服务器渲染的[useSyncExternalStore](https://react.dev/reference/react/useSyncExternalStore)

参考链接

关于作者

作者:Wandra

内容:算法 | 趋势 |源码|Vue | React | CSS | Typescript | Webpack | Vite | GithubAction | GraphQL | Uniqpp。

专栏:欢迎关注呀🌹

本专栏致力于每周分享一个项目,如果本文对你有帮助的话,欢迎点赞或者关注☘️