React Hook 钩子 useInsertionEffect、useLayoutEffect、useEffect的区别

要搞懂 useInsertionEffectuseLayoutEffectuseEffect 的区别,核心是抓住 执行时机适用场景 ------ 这三个 Hook 都是 React 处理"副作用"的工具,但执行阶段、阻塞特性、适用场景完全不同,选对了能避免渲染闪烁、样式失效等问题。

先看一张核心对比表,快速建立认知:

特性 useEffect useLayoutEffect useInsertionEffect
执行时机 组件渲染完成(屏幕绘制后) 组件渲染完成(屏幕绘制前) DOM 生成后、布局计算前(React 18+ 新增)
是否阻塞渲染 不阻塞(异步) 阻塞(同步) 阻塞(同步,更早)
能否操作 DOM 能(但可能导致闪烁) 能(无闪烁) 能(仅用于插入样式)
触发时机排序 最晚 中间 最早
核心用途 数据请求、事件监听、异步操作 DOM 测量、同步修改 DOM、避免闪烁 CSS-in-JS 插入样式、避免样式计算重复

一、逐个拆解:执行时机 + 核心用法

1. useEffect(最常用)
  • 执行时机 :组件首次渲染/依赖更新后 → 浏览器完成屏幕绘制 → 执行 useEffect 回调(异步,不阻塞页面显示)。

  • 核心特点:和浏览器"绘制"解耦,是 React 推荐的默认副作用 Hook。

  • 适用场景

    • 数据请求(接口调用)、异步操作;
    • 订阅/取消订阅(如事件监听、WebSocket);
    • 不依赖 DOM 布局的操作(如修改状态、记录日志)。
  • 示例

    js 复制代码
    import { useEffect, useState } from 'react';
    
    function UserList() {
      const [users, setUsers] = useState([]);
    
      // 组件渲染完成后请求数据(不阻塞页面)
      useEffect(() => {
        const fetchUsers = async () => {
          const res = await fetch('/api/users');
          setUsers(await res.json());
        };
        fetchUsers();
    
        // 清理副作用(组件卸载时取消监听)
        return () => {
          // 比如取消定时器、移除事件监听
        };
      }, []); // 空依赖:仅首次渲染执行
    
      return <div>{users.map(u => <p key={u.id}>{u.name}</p>)}</div>;
    }
2. useLayoutEffect(同步操作 DOM)
  • 执行时机 :组件首次渲染/依赖更新后 → React 完成 DOM 变更 → 浏览器未绘制屏幕 → 同步执行 useLayoutEffect 回调 → 执行完成后浏览器才绘制屏幕(阻塞绘制)。

  • 核心特点 :比 useEffect 早执行,同步阻塞渲染,能避免 DOM 操作导致的"视觉闪烁"。

  • 适用场景

    • DOM 测量(如获取元素宽高、位置)并同步修改 DOM;
    • 避免闪烁的样式修改(比如根据元素位置调整弹窗位置);
    • 依赖 DOM 布局的同步操作。
  • 示例(解决测量 DOM 导致的闪烁)

    js 复制代码
    import { useLayoutEffect, useRef, useState } from 'react';
    
    function Popup() {
      const [width, setWidth] = useState(0);
      const ref = useRef(null);
    
      // useLayoutEffect:绘制前测量,无闪烁
      useLayoutEffect(() => {
        // 同步获取 DOM 宽度(此时 DOM 已生成,未绘制)
        const w = ref.current.offsetWidth;
        setWidth(w); // 修改状态后,React 会在绘制前重新计算布局
      }, []);
    
      // 若用 useEffect:绘制后测量,会先显示 0,再更新为实际宽度(闪烁)
      // useEffect(() => { ... }, []);
    
      return <div ref={ref}>弹窗宽度:{width}px</div>;
    }
3. useInsertionEffect(React 18+ 新增,专用于样式插入)
  • 执行时机 :React 生成 DOM 节点后 → useLayoutEffect 执行前 → 浏览器计算布局/绘制前(是三个 Hook 中最早执行的)。

  • 核心特点 :专为 CSS-in-JS 库设计,解决"样式插入时机晚于布局计算"导致的性能问题,只能用于插入样式,不能修改状态(修改状态会触发额外渲染)。

  • 适用场景

    • CSS-in-JS 库插入动态样式(如 styled-components、emotion);
    • 需在布局计算前插入样式,避免浏览器重复计算布局。
  • 示例(CSS-in-JS 插入样式)

    js 复制代码
    import { useInsertionEffect } from 'react';
    
    // 自定义 CSS-in-JS 样式插入 Hook
    function useInjectStyle(style) {
      useInsertionEffect(() => {
        // 布局计算前插入样式,避免重复计算
        const styleTag = document.createElement('style');
        styleTag.innerHTML = style;
        document.head.appendChild(styleTag);
    
        return () => {
          document.head.removeChild(styleTag);
        };
      }, [style]);
    }
    
    function StyledButton() {
      // 插入动态样式
      useInjectStyle(`
        .btn {
          background: blue;
          color: white;
          padding: 8px 16px;
        }
      `);
    
      return <button className="btn">自定义样式按钮</button>;
    }

二、关键执行顺序(实战必懂)

以组件首次渲染为例,完整执行流程:

复制代码
1. React 渲染组件 → 生成 DOM 节点(未布局、未绘制)
2. 执行 useInsertionEffect 回调(仅插入样式,不修改状态)
3. 浏览器计算 DOM 布局(宽高、位置)
4. 执行 useLayoutEffect 回调(同步修改 DOM/状态,可能触发重新布局)
5. 浏览器绘制屏幕(页面显示)
6. 执行 useEffect 回调(异步,不阻塞)

三、避坑指南(新手常错)

1. 不要滥用 useLayoutEffect

useLayoutEffect 阻塞绘制,若回调内逻辑复杂(如大量计算),会导致页面加载变慢。能用 useEffect 解决的,就不用 useLayoutEffect

2. useInsertionEffect 不能修改状态

useInsertionEffect 执行时,React 还未完成"布局提交",修改状态会触发额外的布局计算,导致性能问题 ------ 它的唯一用途是插入样式。

3. 依赖项的坑

三个 Hook 的依赖项规则一致:

  • 空数组 []:仅首次渲染执行;
  • 依赖变量:变量变化时执行;
  • 无依赖:每次渲染都执行。
4. 服务端渲染(SSR)注意
  • useInsertionEffect 是唯一能在 SSR 中安全插入样式的 Hook(useLayoutEffect 在 SSR 中会报警告);

  • 若用 useLayoutEffect 做 SSR,需加判断:

    js 复制代码
    const isClient = typeof window !== 'undefined';
    useLayoutEffect(() => {
      if (isClient) {
        // 仅客户端执行 DOM 操作
      }
    }, []);

总结

  1. 选 useEffect:绝大多数场景(数据请求、异步操作、事件监听),默认首选;
  2. 选 useLayoutEffect:需要同步操作 DOM、测量布局、避免视觉闪烁时;
  3. 选 useInsertionEffect:React 18+ 中做 CSS-in-JS 样式插入,或需在布局计算前插入样式时。

核心记忆点:

  • 执行顺序:useInsertionEffectuseLayoutEffectuseEffect
  • 阻塞性:前两个阻塞渲染,useEffect 不阻塞;
  • 专用性:useInsertionEffect 仅用于样式插入,不要挪作他用。

如果有具体场景(比如"修改样式导致闪烁""CSS-in-JS 样式不生效"),可以贴出代码,我帮你选对应的 Hook 并优化。

相关推荐
坚持学习前端日记2 小时前
Agent AI 后端接口对接与大模型适配指南
前端·人工智能·python·ios
坚持学习前端日记2 小时前
Agent AI 多模态交互与全场景架构设计
前端·javascript·人工智能·visual studio
王家视频教程图书馆2 小时前
vue3移动端组件库清单
前端
毕设源码-郭学长2 小时前
【开题答辩全过程】以 基于web的车辆检测管理系统的设计与实现为例,包含答辩的问题和答案
前端
向上的车轮2 小时前
TypeScript 一日速通指南:以订单管理系统实战为核心
前端·javascript·typescript
大雷神2 小时前
HarmonyOS APP<玩转React>开源教程七:HarmonyOS 数据存储方案
react.js·开源·harmonyos
yqzyy2 小时前
Nginx 配置:alias 和 root 的区别
前端·javascript·nginx
冰糖雪梨dd2 小时前
【JavaScript】 substring()方法详解
开发语言·前端·javascript
John Song2 小时前
npm查看全局安装了哪些命令
前端·npm·node.js