面试:React 中的 useEffect 与 useLayoutEffect有什么区别

介绍🌟

随着 React 16.8 中 hooks 的引入,编写函数式组件的景观发生了巨大变化。 Among 这些,useEffectuseLayoutEffect 尤为重要,用于处理代码中的副作用。乍看之下,它们可能看起来很相似,但两者之间存在一些重要区别。在本博文中,我们将以清晰而直接的方式来看这些差异。我们将帮助您理解何时使用 useEffect 以及何时 useLayoutEffect 可能是 React 项目的更好选择。

让我们开始这个实用的探索,深入了解这两个必不可少的 React hooks。

useEffect 是什么?🔍

useEffect 是 React 的一个钩子,允许您在函数组件中执行副作用。副作用本质上是指影响执行函数范围之外的操作。这可能包括数据获取、订阅、手动更改 DOM 等。

记住,99% 的时候,useEffect 是您想要使用的钩子。

**如何使用 useEffect:**🚀

当您使用 useEffect 时,您告诉 React 在渲染后做一些事情。React 会记住您传递的函数,并在执行 DOM 更新后调用它。

useEffect 的语法:

useEffect(() => {
  // 您的副作用代码放在这里。 
}, [dependencies]);

第二个参数 [dependencies] 是依赖关系数组。React 只会在依赖项之一发生变化时重新运行副作用。如果您传递一个空数组([]),副作用会在初始渲染后只运行一次,这与 class 组件中的 componentDidMount 类似。

useLayoutEffect 是什么?🤔

useLayoutEffect 是与 useEffect 类似的另一个 React 钩子,但在其执行时机上有一个关键区别。 它用于需要与 DOM 同步的操作,比如直接操作 DOM 或在浏览器绘制前调整布局。

**何时使用 useLayoutEffect:**📐

如果您需要在屏幕更新前进行 DOM 更改,则应使用 useLayoutEffect。 在您想要避免操作 DOM 后可能发生的视觉抖动(如闪烁)的情况下,这一点至关重要。

useLayoutEffect 的语法:

useLayoutEffect(() => {
  // 与 DOM 交互的代码
}, [dependencies]);

useEffect 一样,它也将依赖关系数组作为第二个参数。

比较 useEffect 和 useLayoutEffect 的行为⚖️

在分别探讨了 useEffectuseLayoutEffect 之后,在同一个组件中看到它们的行动是很有帮助的。 这种比较将使我们更清楚地了解它们的执行顺序和行为。

让我们创建一个同时包含 useEffectuseLayoutEffect 的单个函数组件。 我们将在每个钩子中添加控制台日志,以观察它们执行的顺序。

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

const Home = () => {   
  
  useLayoutEffect(() => {
    console.log('useLayoutEffect - 首先运行,但在 DOM 变更后'); 
  }, []);   

  useEffect(() => {
    console.log('useEffect - 其次运行,浏览器绘制后');
  }, []);

  return (  
    <div>Hello, React Hooks!</div>  
  );  
};  

export default Home;

在此组件中,我们同时具有依赖关系数组均为空的 useLayoutEffectuseEffect,这意味着它们应该在初始渲染后运行。

useEffect 对比 useLayoutEffect

  1. useLayoutEffect: 此钩子首先运行。它在所有 DOM 变更完成后但浏览器有机会绘制之前触发。 这使得它非常适合需要在 DOM 更新后但用户看到任何内容之前发生的任何 DOM 操纵或计算。 useLayoutEffect 内的控制台日志将是首先出现的。
  2. useEffect: 此钩子在 useLayoutEffect 之后运行。它在组件渲染周期完成并且屏幕更新后触发。 这种行为意味着 useEffect 最适合不需要立即同步更新 DOM 的任务,比如 API 调用或设置订阅。 useEffect 内的控制台日志将出现在来自 useLayoutEffect 的日志之后。

理论上,由于其异步性质(在浏览器绘制后执行),useEffect 可能会导致闪烁效果,但在实践中,观察到明显的闪烁可能很困难。 从视觉上看,useEffectuseLayoutEffect 的行为通常相似,尤其是对于简单的 DOM 更新。

实际例子:useEffect 和 useLayoutEffect 的行动💫

为了更好地掌握 useEffectuseLayoutEffect 的细微差别,让我们深入一些实际例子。 这些将说明每个 hook 如何在不同情况下被有效利用。

重要的是要注意,在许多情况下,尤其是在简单的场景或使用现代高性能浏览器的情况下,这些差异可能不会在视觉上明显。

1️⃣ useEffect 用于数据获取🌐📈

数据获取是 useEffect 的常见用例。 它允许您在组件渲染时从 API 请求和加载数据。

这是一个完整的 React 组件示例,它使用 useEffectJSONPlaceholder API(一个免费的假在线 REST API)获取数据:

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

const App = () => {  
  const [data, setData] = useState([]);   

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch('https://jsonplaceholder.typicode.com/posts');  
        const jsonData = await response.json();
        setData(jsonData);  
      } catch (error) {
        console.error('Error fetching data: ', error);
      }
    };
    fetchData();
  }, []);  
  return (
    <div>
      <h1>从 JSONPlaceholder API 获取的数据</h1>  
      <ul>
        {data.map(item => (  
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
    </div>
  );
};

export default App;

该组件渲染从 API 获取的标题列表。 useEffect 中依赖关系列表中的空数组确保数据获取仅运行一次,类似于 class 组件中的 componentDidMount

2️⃣ useLayoutEffect 用于 DOM 操作:📜

让我们考虑一种情况,您想要在一些状态更改后立即将列表的滚动位置调整为顶部。 用 useEffect 做这件事可能会导致明显的闪烁,因为调整是在屏幕更新后发生的。 useLayoutEffect 通过确保调整在屏幕绘制之前进行来解决这个问题。

这是一个例子:

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

const App = () => {
  const [items, setItems] = useState([]);
  const listRef = useRef(null);

  const addItems = () => {
    const newItems = [...Array(5).keys()].map(i => `Item ${i + items.length}`);
    setItems([...items, ...newItems]);
  };

  useLayoutEffect(() => {
    // 在屏幕更新之前调整滚动位置
    if (listRef.current) {
      listRef.current.scrollTop = 0;
    }
  }, [items]); // 依赖于“items”状态

  return (
    <div>
      <button onClick={addItems}>添加项目</button>
      <ul ref={listRef}> 
        {items.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
};

export default App;

在这个组件中:

  • 我们使用 useState 创建一个 items 状态来存储项目列表。
  • 提供了一个按钮来向列表中添加新项目。
  • 使用 useLayoutEffect 钩子在添加新项目时将列表滚动到顶部。这是在浏览器绘制更新的 UI 之前完成的,防止滚动位置的任何闪烁或跳转。
  • 使用 ref 属性(listRef)来引用列表 DOM 元素以操纵其滚动位置。

这个例子展示了如何使用 useLayoutEffect 在响应 React 组件中的状态更改时平滑地、无闪烁地调整 DOM。

结论✅

useEffectuseLayoutEffect 之间的选择通常取决于正在执行的 DOM 操作的具体要求。 尽管视觉差异不总是明显,但理解这些钩子的内部行为对于优化 React 应用程序的性能和确保顺畅的用户体验至关重要。

对于大多数副作用,特别是那些不需要与 DOM 立即交互的副作用(如数据获取或设置订阅),useEffect 是您的首选钩子。 其对状态和道具更改做出反应的能力使其对广泛的用例非常通用。

另一方面,当您需要根据 DOM 更改进行同步更新时,useLayoutEffect 是必不可少的。 当操作 DOM 时,它是防止视觉故障并确保顺畅用户体验的完美工具。

编码快乐!

  • 35
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
useEffectuseLayoutEffectReact的两个钩子函数,它们用于在组件渲染完成后执行一些副作用操作。它们的主要区别在于执行时机和对页面渲染的影响。 useEffect的回调函数会在浏览器执行绘制之后被调用,它是异步执行的,不会阻塞渲染过程。因此,如果副作用操作不涉及对DOM的操作或页面交互,或者可以接受一定的延迟,可以使用useEffectuseLayoutEffect的回调函数会在DOM更新之后,浏览器执行绘制之前被调用。它在执行回调函数时会产生阻塞效果,可能导致页面感觉卡顿。因此,如果副作用操作需要操作DOM或进行与界面交互相关的操作,可以考虑使用useLayoutEffect。 综上所述,优先使用useEffect,因为它是异步执行的,不会阻塞渲染。只有当需要进行DOM操作或与界面交互的操作时,才考虑使用useLayoutEffect。但需要注意,useLayoutEffect在服务端渲染时可能会导致首屏实际内容和服务端渲染出来的内容不一致,会有一个警告。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [四、useEffectuseLayoutEffect深入学习超大章](https://blog.csdn.net/NinthMonee/article/details/113124449)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [useEffectuseLayoutEffect的基础知识和底层机制](https://blog.csdn.net/qq_52563729/article/details/131155613)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [useEffectuseLayoutEffect](https://blog.csdn.net/qq_37548296/article/details/126471059)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

今天也想MK代码

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值