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 并优化。

相关推荐
Setsuna_F_Seiei23 分钟前
前端转型 Agent 开发 05 之 Agent Hooks 与 Checkpointer(让 Agent 从全自动转变人为可掌控)
前端·agent·ai编程
百万蹄蹄向前冲2 小时前
风扇转了一晚上MVP专家团翻车事故
前端·人工智能
默_笙2 小时前
🏛 给 AI 配一间办公室:Harness Engineering 六大模块与它的实现
前端·javascript
linux_cfan4 小时前
videojs v10 源代码系列解读:14 · 谓词守卫:在运行时安全地调用能力
前端·javascript·音视频
狗狗狗狗狗乐啊4 小时前
搭一个 AI 对话工作台 AChat:从 0 到可用的完整记录(一)
python·react.js·ai编程
kyriewen4 小时前
我扒了 10,221 条 JD:腾讯技术岗 75% 在要 AI
前端·人工智能·ai编程
郑州光合科技余经理5 小时前
同城外卖小程序开发:下单成功后,后台导出能不能对上用户端状态
开发语言·前端·git·后端·uni-app·php·ai编程
IT_陈寒5 小时前
Vue的响应式让我熬到凌晨三点,原来漏了这个小细节
前端·人工智能·后端
Blanche15006 小时前
利用 RAG 为答疑机器人扩展知识范围
前端
天若有情6736 小时前
【纯前端小工具】公历生日转农历,批量查询每年农历生日对应的公历日期(GitHub Pages在线直接用)
前端·javascript·github pages·农历转换·lunisolar·网页小工具