「React」useEffect 与 useLayoutEffect 使用与区别

前言

useEffect 与 useLayoutEffect 是两个 Hooks,前者比较常用,后者在一些场景下也会用到,下面说明两者区别和应用场景。
在这里插入图片描述

使用

useEffectuseLayoutEffect 是React Hooks里用于处理副作用的钩子(Hooks),它们看起来非常相似,但实际上在何时执行副作用上有细微差别,这影响到了它们的使用场景。

useEffect

useEffect 是在组件渲染到屏幕之后异步执行的。这意味着它不会阻塞浏览器的绘制过程,可以用于大多数副作用场景,比如数据获取、订阅或者手动修改DOM当组件更新后等。

  • 使用场景: 用于大部分没有严格的执行时序要求的副作用操作。
  • 执行时机: 在全部DOM变更完成后,浏览器绘制之后异步执行。
  • 影响: 因为是异步执行,不会阻塞页面的可视化渲染。

useLayoutEffect

useLayoutEffect 的执行时机更接近于类组件的 componentDidMountcomponentDidUpdate 生命周期方法。它在DOM更新完成后同步执行,但在浏览器进行绘制前。

  • 使用场景: 适用于需要同步调整布局的副作用操作。如果需要在DOM变化后立即同步执行一些操作(比如读取DOM布局并同步重绘),就可以用 useLayoutEffect
  • 执行时机: 在所有DOM变更之后同步执行,但在浏览器绘制之前。
  • 影响: 因为阻塞了浏览器的绘制,如果执行复杂或长时间运算,可能会导致性能问题或视觉上的卡顿。

区别

  • 执行时机: useEffect 是异步执行,不会阻塞浏览器的绘制;而 useLayoutEffect 是同步执行,在所有DOM变更之后立即运行,但这会延迟浏览器的绘制。
  • 适用场景: 一般推荐默认使用 useEffect,只有在涉及到需要在布局渲染阶段同步执行的DOM操作或有严格的顺序要求时,才使用 useLayoutEffect

由于 useLayoutEffect 会在绘制前执行,如果操作耗时过长,可能会导致用户感觉到卡顿。因此,除非必要,否则建议优先使用 useEffect

例子

当然可以。这里我会演示一个简单的例子,其中useLayoutEffectuseEffect将有不同的效果。

考虑以下场景:我们有一个要隐藏的消息,并且有一个按钮用于显示这个消息。如果我们使用useLayoutEffect去更改消息状态,用户界面不会显示任何隐藏-再显示的过渡效果,因为所有的更改都是在浏览器绘制之前发生的。相反,如果我们使用useEffect,那么可能会短暂看到消息从隐藏状态变为显示状态的过渡,因为useEffect是在组件渲染和浏览器绘制完成后才执行。

useLayoutEffect的行为

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

function LayoutEffectDemo() {
  const [message, setMessage] = useState('这是一条消息');
  const [hidden, setHidden] = useState(true);

  useLayoutEffect(() => {
    if (hidden) {
      setMessage('这是一条消息');
    }
  }, [hidden]);

  return (
    <div>
      {!hidden && <p>{message}</p>}
      <button onClick={() => setHidden(false)}>显示信息</button>
    </div>
  );
}

在这个例子中,“这是一条消息”将会在按钮点击后立即正确显示,用户不会看到任何视觉变化之前消息的状态。

useEffect的行为

现在,我们用相同的例子但用useEffect来替代useLayoutEffect

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

function EffectDemo() {
  const [message, setMessage] = useState('');
  const [hidden, setHidden] = useState(true);

  useEffect(() => {
    if (hidden) {
      setMessage('这是一条消息');
    }
  }, [hidden]);

  return (
    <div>
      {!hidden && <p>{message}</p>}
      <button onClick={() => setHidden(false)}>显示信息</button>
    </div>
  );
}

在使用useEffect的版本中,按钮点击后状态更新会导致组件的一个渲染周期,在这个周期中消息是隐藏的。然后useEffect会在这个渲染周期之后运行,并且消息会变成可见的。这个过程虽然非常快,但理论上可以被用户观察到,这就是为什么有的时候你会看到界面闪烁。

每一个钩子都有其用例,而显示不必要的用户界面变化(闪烁等)是应该避免的,通常是通过useLayoutEffect来处理,因为它在所有DOM变动之后同步执行,而且在浏览器进行绘制之前,从而保证用户看不到中间状态。

### React 中 `useEffect` `useLayoutEffect` 的区别 #### 1. 执行时机的不同 `useEffect` 是在浏览器完成布局绘制之后执行副作用操作的钩子[^1]。这意味着当组件已经渲染到屏幕上并显示给用户时,`useEffect` 定义的回调才会被执行。 相比之下,`useLayoutEffect` 则是在浏览器完成布局计算但尚未将其绘制到屏幕上的阶段同步执行其定义的副作用逻辑[^3]。因此,如果某些副作用需要立即更新 DOM 并阻止视觉闪烁或不一致的情况发生,则应该优先考虑使用 `useLayoutEffect`。 #### 2. 性能影响方面 由于 `useLayoutEffect` 同步阻塞了页面重绘过程中的时间窗口,所以它可能会带来一定的性能开销;而异步性质使得 `useEffect` 更适合处理那些不会直接影响当前帧呈现效果的任务,比如网络请求、订阅事件等非紧急型工作负载[^4]。 以下是两个例子来展示它们如何应用: ```javascript import React, { useState, useEffect } from 'react'; function ExampleComponent() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } ``` 在这个简单的计数器示例里,每当点击按钮改变状态变量 count 值的时候都会触发一次 effect 更新网页标题栏文字内容 —— 这种场景下显然无需担心即时性问题故采用标准版即可满足需求. 对于另一种情况: ```javascript import React, { useLayoutEffect, useRef } from 'react'; const MeasureExample = () => { const ref = useRef(); useLayoutEffect(() => { console.log(ref.current.getBoundingClientRect()); }, []); return <div ref={ref}>Hello World!</div>; }; export default MeasureExample; ``` 这里通过 `useLayoutEffect`, 可以确保测量行为发生在重新绘制前,从而避免可能因尺寸变化引起的闪屏现象. 综上所述,尽管两者都能用来管理函数式组件内的各种 side-effects ,但在实际开发过程中还是应当依据具体业务需求合理选用合适的工具选项.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zoe_ya

如果你成功申请,可以打赏杯奶茶

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值