React 中hooks之useLayoutEffect 用法总结以及与useEffect的区别

devtools/2025/1/17 20:52:00/

React useLayoutEffect

1. useLayoutEffect 基本概念

useLayoutEffect 是 React 的一个 Hook,它的函数签名与 useEffect 完全相同,但它会在所有的 DOM 变更之后同步调用 effect。它可以用来读取 DOM 布局并同步触发重渲染。

2. useLayoutEffect vs useEffect

2.1 执行时机对比

Hook 名称执行时机执行方式使用场景
useEffectDOM 更新后且浏览器重新绘制屏幕之后异步执行 (组件渲染完成后)异步执行,不阻塞浏览器渲染大多数副作用,如数据获取、订阅
useLayoutEffectDOM 更新后且浏览器重新绘制屏幕之前同步执行(组件将要渲染时)同步执行,会阻塞浏览器渲染需要同步测量 DOM 或更新布局

2.2 执行顺序示例

function ExampleComponent() {const [count, setCount] = useState(0);useEffect(() => {console.log('useEffect 执行'); // 后执行});useLayoutEffect(() => {console.log('useLayoutEffect 执行'); // 先执行});return (<div onClick={() => setCount(c => c + 1)}>点击次数:{count}</div>);
}

3. useLayoutEffect 使用场景

3.1 DOM 测量和更新

function AutoHeight() {const [height, setHeight] = useState(0);const elementRef = useRef();useLayoutEffect(() => {// 在 DOM 更新后立即测量高度const element = elementRef.current;const elementHeight = element.getBoundingClientRect().height;if (elementHeight !== height) {// 立即更新高度,避免闪烁setHeight(elementHeight);}}, [height]);return (<div><div ref={elementRef} style={{ height: height || 'auto' }}>内容</div><div>当前高度: {height}px</div></div>);
}

3.2 防止闪烁的工具提示

function Tooltip({ text, position }) {const tooltipRef = useRef();const [tooltipPosition, setTooltipPosition] = useState(position);useLayoutEffect(() => {const tooltip = tooltipRef.current;const rect = tooltip.getBoundingClientRect();// 检查是否超出视口if (rect.right > window.innerWidth) {// 立即调整位置,避免闪烁setTooltipPosition({...position,left: position.left - (rect.right - window.innerWidth)});}}, [position]);return (<divref={tooltipRef}style={{position: 'absolute',...tooltipPosition}}>{text}</div>);
}

3.3 动画处理

function AnimatedComponent() {const elementRef = useRef();const [isVisible, setIsVisible] = useState(false);useLayoutEffect(() => {if (isVisible) {const element = elementRef.current;// 立即设置初始状态element.style.opacity = '0';element.style.transform = 'translateY(20px)';// 强制重排element.getBoundingClientRect();// 应用动画element.style.transition = 'opacity 0.5s, transform 0.5s';element.style.opacity = '1';element.style.transform = 'translateY(0)';}}, [isVisible]);return (<div><button onClick={() => setIsVisible(true)}>显示</button><div ref={elementRef}>动画内容</div></div>);
}

3.4 滚动位置同步

function ScrollSync({ content }) {const scrollContainerRef = useRef();useLayoutEffect(() => {const element = scrollContainerRef.current;// 内容更新后立即滚动到底部element.scrollTop = element.scrollHeight;}, [content]); // 当内容更新时执行return (<div ref={scrollContainerRef}style={{ height: '200px', overflow: 'auto' }}>{content.map((item, index) => (<div key={index}>{item}</div>))}</div>);
}

3.5 Modal 定位

function Modal({ isOpen, children }) {const modalRef = useRef();useLayoutEffect(() => {if (isOpen) {const modalElement = modalRef.current;const viewportHeight = window.innerHeight;const modalHeight = modalElement.getBoundingClientRect().height;// 立即计算并设置最佳位置modalElement.style.top = \`\${Math.max(0,(viewportHeight - modalHeight) / 2)}px\`;}}, [isOpen]);if (!isOpen) return null;return (<div className="modal-overlay"><div ref={modalRef} className="modal">{children}</div></div>);
}

4. 性能考虑

4.1 何时使用 useLayoutEffect

  • 需要同步测量 DOM 元素
  • 需要在视觉更新前进行 DOM 修改
  • 需要避免闪烁或布局抖动
  • 处理依赖于 DOM 布局的动画

4.2 何时使用 useEffect

  • 数据获取
  • 订阅事件
  • 日志记录
  • 其他不需要同步 DOM 测量或修改的副作用

5. 最佳实践

  1. 优先使用 useEffect
// ✅ 大多数情况下使用 useEffect 即可
useEffect(() => {// 异步操作,不影响渲染fetchData();
}, []);
  1. 仅在必要时使用 useLayoutEffect
// ✅ 需要同步 DOM 测量和更新时使用 useLayoutEffect
useLayoutEffect(() => {// 同步操作,立即更新 DOMupdateDOMPosition();
}, []);
  1. 注意性能影响
// ❌ 避免在 useLayoutEffect 中进行耗时操作
useLayoutEffect(() => {// 不要在这里进行大量计算或 API 调用heavyComputation();
}, []);// ✅ 耗时操作应该放在 useEffect 中
useEffect(() => {heavyComputation();
}, []);
  1. 合理使用依赖项
function OptimizedComponent({ data }) {useLayoutEffect(() => {// 只在真正需要同步更新的依赖项发生变化时执行}, [data.layout]); // 只依赖布局相关的属性
}

6. 注意事项

  1. useLayoutEffect 在服务器端渲染(SSR)中会收到警告,因为它只能在客户端执行
  2. 过度使用 useLayoutEffect 可能会导致性能问题
  3. 应该将耗时的操作放在 useEffect 中,只在 useLayoutEffect 中处理视觉相关的同步更新
  4. 在条件语句中使用时需要注意 Hook 规则

通过合理使用 useLayoutEffect 和 useEffect,我们可以更好地控制副作用的执行时机,优化用户体验,同时保持应用的性能。在实际开发中,应该根据具体场景选择合适的 Hook。


http://www.ppmy.cn/devtools/151356.html

相关文章

学习微信小程序的下拉列表控件-picker

1、创建一个空白工程 2、index.wxml中写上picker布局&#xff1a; <!--index.wxml--> <view class"container"><picker mode"selector" range"{{array}}" bindchange"bindPickerChange"><view class"pick…

微软与腾讯技术交锋,TRELLIS引领3D生成领域多格式支持新方向

去年 11 月&#xff0c;腾讯推出 Hunyuan3D 生成模型&#xff0c;是业界首个同时支持文字和图像生成 3D 的开源大模型。紧接着不到一个月&#xff0c;微软便发布了全新框架 TRELLIS&#xff0c;加入 3D 资产生成领域的竞争中。TRELLIS 支持多格式输出&#xff0c;包括辐射场、3…

ubuntu22.04:解决google chrome 访问百度页面加载慢的问题

前因&#xff1a;ubuntu22.04系统&#xff0c;本地有两个浏览器&#xff0c;firefox和chrome&#xff0c;firefox无论是使用代理还是不使用代理访问百度网站的速度都十分的快&#xff0c;chrome即使开代理访问百度网站也很慢&#xff0c;但是访问其他网站却很快。 1、在chrome地…

【2024年华为OD机试】(B卷,100分)- 数据分类 (Java JS PythonC/C++)

一、问题描述 题目描述 对一个数据a进行分类&#xff0c;分类方法为&#xff1a; 此数据a&#xff08;四个字节大小&#xff09;的四个字节相加对一个给定的值b取模&#xff0c;如果得到的结果小于一个给定的值c&#xff0c;则数据a为有效类型&#xff0c;其类型为取模的值&…

web程序防止xss攻击和跨域

什么是XSS攻击&#xff1f; xss攻击是指有人恶意编写了一段脚本链接&#xff0c;当用户点击的时候就会向用户所在的服务器发送一个请求&#xff0c;这个请求包含了一段js代码&#xff0c;如果不防止xss那么这段js代码会被执行&#xff0c;这样子后果十分严重&#xff0c;容易被…

Jenkins-简介/安装!

一. 关于持续集成&#xff1a; 持续集成(CI ) [ Continuous Integration ]&#xff0c;通俗来讲&#xff0c;就是一个能监控版本控制系统变化的工具&#xff0c;可以自动编译和测试集成的应用程序。出现问题&#xff0c;能够及时的通知相应人员。持续集成是一种思维工具集&…

【Flink系列】4. Flink运行时架构

4. Flink运行时架构 4.1 系统架构 Flink运行时架构——Standalone会话模式为例 1&#xff09;作业管理器&#xff08;JobManager&#xff09; JobManager是一个Flink集群中任务管理和调度的核心&#xff0c;是控制应用执行的主进程。也就是说&#xff0c;每个应用都应该被…

【Uniapp-Vue3】pages设置页面路径及窗口表现

在pages.json中可以看到创建的页面的信息&#xff1a; 数组中的每一个对象就是一个页面的信息&#xff0c;每个页面的信息设置可以在style中设置&#xff0c;常用的样式设置&#xff1a;