React:useLayoutEffect 解决组件闪烁问题

发布于:2025-03-26 ⋅ 阅读:(16) ⋅ 点赞:(0)

React useLayoutEffect 钩子详解

useLayoutEffect 是 React 中的一个钩子函数,它与更常用的 useEffect 非常相似,但有一个关键区别:执行时机不同

useLayoutEffect 的工作原理

useLayoutEffect 的执行过程如下:

  1. React 渲染组件,计算 DOM 更新
  2. DOM 变更被应用到屏幕(但用户还看不到)
  3. useLayoutEffect 同步执行
  4. 浏览器绘制更新后的 DOM(用户看到内容)

useEffect 的执行过程是:

  1. React 渲染组件,计算 DOM 更新
  2. DOM 变更被应用到屏幕
  3. 浏览器绘制更新后的 DOM(用户看到内容)
  4. useEffect 异步执行

使用场景

useLayoutEffect 主要适用于以下场景:

  1. 防止视觉闪烁:当你需要在用户看到页面之前进行 DOM 测量和修改时
  2. 同步 DOM 操作:需要在浏览器绘制之前同步更新 DOM
  3. 依赖于 DOM 测量的动画:需要测量元素尺寸并立即应用动画效果

代码示例

基本用法

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

function MeasureExample() {
  const divRef = useRef();
  const [dimensions, setDimensions] = React.useState({ width: 0, height: 0 });
  
  useLayoutEffect(() => {
    // 这里的测量和更新会在浏览器绘制前完成,避免闪烁
    const { width, height } = divRef.current.getBoundingClientRect();
    setDimensions({ width, height });
  }, []);
  
  return (
    <div>
      <div ref={divRef}>测量我的尺寸</div>
      <p>宽度: {dimensions.width}px, 高度: {dimensions.height}px</p>
    </div>
  );
}

防止闪烁的例子

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

function PreventFlickerExample() {
  const [position, setPosition] = useState('left');
  
  useLayoutEffect(() => {
    // 如果使用普通的useEffect,会先看到元素在左边,然后跳到右边
    // 使用useLayoutEffect可以确保用户只看到最终位置
    if (position === 'left') {
      setPosition('right');
    }
  }, [position]);
  
  return (
    <div style={{ 
      position: 'relative', 
      height: '100px', 
      width: '100%' 
    }}>
      <div style={{ 
        position: 'absolute', 
        left: position === 'left' ? '0px' : '300px',
        transition: 'left 0.5s'
      }}>
        移动的方块
      </div>
    </div>
  );
}

useLayoutEffect vs useEffect

特性 useLayoutEffect useEffect
执行时机 DOM 更新后,浏览器绘制前(同步) 浏览器绘制后(异步)
阻塞渲染
适用场景 DOM 测量和修改,防止闪烁 数据获取,订阅,手动DOM修改
性能影响 可能延迟视觉更新 对渲染性能影响小

最佳实践

  • 默认使用 useEffect:除非有特殊需求,否则优先使用 useEffect
  • 仅在必要时使用 useLayoutEffect:当需要在用户看到页面之前进行DOM操作时
  • 保持回调函数简洁:由于会阻塞渲染,应避免在 useLayoutEffect 中执行耗时操作