返回

<br/>巧用useEffect和useLayoutEffect:用通俗语言探索二者差异

前端

useEffect 与 useLayoutEffect:React 副作用管理的差异

引言:

在 React 生态系统中,副作用是指任何在组件渲染之外发生的活动。为了管理这些副作用,React 提供了两种强大的钩子函数:useEffect 和 useLayoutEffect。在本文中,我们将深入探讨这些钩子函数之间的差异,指导你何时使用每个函数。

1. 执行时机的区别

useEffect:

  • 在组件渲染后执行
  • 在组件销毁前执行清理函数

useLayoutEffect:

  • 在浏览器渲染 DOM 后执行
  • 在组件销毁前执行清理函数

2. 主要应用场景的差异

useEffect:

  • 处理大多数副作用,例如数据获取、定时器等可以在渲染后安全执行的操作。

useLayoutEffect:

  • 处理与 DOM 交互的副作用,例如更新滚动条位置、测量元素尺寸等必须在 DOM 渲染后才能执行的操作。

3. 何时使用 useEffect?

  • 当你需要在渲染后执行副作用时,例如:
    • 通过 API 获取数据
    • 设置定时器
    • 在组件卸载时执行清理函数
  • 当你需要在组件卸载时执行清理函数时,例如:
    • 取消定时器
    • 删除事件监听器
    • 清除内存泄漏

4. 何时使用 useLayoutEffect?

  • 当你需要在浏览器渲染 DOM 后执行副作用时,例如:
    • 更新滚动条位置
    • 测量元素尺寸
  • 在组件卸载时执行清理函数,例如:
    • 删除事件监听器
    • 清除内存泄漏
  • 避免因 useEffect 中的 DOM 操作导致的闪烁

示例探究

考虑一个简单的计数器组件,单击按钮时计数器加 1。

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

const Counter = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    // 在每次渲染后更新计数器
    console.log("useEffect: 更新计数器");
  });

  const handleClick = () => {
    setCount((prevCount) => prevCount + 1);
  };

  return (
    <>
      <h1>{count}</h1>
      <button onClick={handleClick}>+</button>
    </>
  );
};

export default Counter;

在这种情况下,使用 useEffect 来更新计数器会导致不必要的开销,因为计数器在每次渲染时都会重新计算。为了解决这个问题,我们可以使用 useLayoutEffect:

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

const Counter = () => {
  const [count, setCount] = useState(0);

  useLayoutEffect(() => {
    // 在 DOM 渲染后更新计数器(仅在挂载时执行)
    console.log("useLayoutEffect: 更新计数器");
  }, []);

  const handleClick = () => {
    setCount((prevCount) => prevCount + 1);
  };

  return (
    <>
      <h1>{count}</h1>
      <button onClick={handleClick}>+</button>
    </>
  );
};

export default Counter;

使用 useLayoutEffect,计数器只会在组件挂载时计算一次,从而提高性能。

总结

useEffect 和 useLayoutEffect 都是 React 中处理副作用的强大工具,但它们的行为有所不同。useEffect 会在组件渲染后执行,useLayoutEffect 会在浏览器渲染 DOM 后执行。

我们应该根据副作用的类型来选择合适的钩子函数。对于大多数副作用,我们可以使用 useEffect。对于与 DOM 交互的副作用,我们应该使用 useLayoutEffect。

常见问题解答

1. useEffect 和 useLayoutEffect 之间的主要区别是什么?

  • 执行时机: useEffect 在渲染后执行,而 useLayoutEffect 在 DOM 渲染后执行。

2. 为什么在 useEffect 中避免 DOM 操作?

  • useEffect 中的 DOM 操作会导致闪烁,因为每次渲染都会执行它们。

3. useLayoutEffect 是否总是比 useEffect 更好?

  • 不,只有在需要在 DOM 渲染后执行副作用时才使用 useLayoutEffect。

4. useEffect 可以用来替代 componentDidMount 和 componentWillUnmount 吗?

  • 是的,useEffect 可以同时替代 componentDidMount 和 componentWillUnmount。

5. 如何防止 useEffect 中的无限循环?

  • 在依赖项数组中使用空数组 [],以防止 useEffect 在每次渲染时执行。