React useEffect 原理与副作用管理详解

作者:小积木时间:2026-10-25 01:22:57浏览:272

本文深入解析 React16.8 引入的 useEffect Hook。通过通俗示例阐述副作用概念,详解依赖数组控制执行频率的方法,并重点剖析清除函数的执行时机与闭包陷阱。文章结合代码片段,说明如何避免无限循环渲染及正确管理组件生命周期中的副作用。

React16.8 版本引入了 useEffect 这个 Hook 函数,专门用于处理组件中的副作用逻辑。

所谓“副作用”,可以用一个生活化的例子来理解:比如感冒后服药本应痊愈,但身体却出现了过敏反应,这种非预期的额外反应即为副作用。

在 React 组件中,主要任务是渲染 DOM 并展示内容。然而,除了 DOM 更新外,往往还需要从外部数据源获取数据,这个“获取外部数据”的过程就属于副作用。

关于 useEffect 的具体用法,官方文档已有详细示例。本文重点总结在实际使用 useEffect 时常见的问题及解决方案。

避免重复循环渲染

useEffect 接收一个数组作为第二个参数,该数组作为依赖项(dependence)。每次 DOM 渲染完成后,React 会浅比较依赖项在渲染前后的值。如果值不一致,则执行副作用函数;如果一致,则不执行。当依赖项为空数组 [] 时,表示没有传入比较变化的变量,此时副作用逻辑仅在组件挂载时执行一次。


useEffect(() => {
  setTimeout(() => {    
        setCounter(counter + 1);  
    }, 300)
}, []);

此外,若希望通过点击刷新按钮获取外部数据且避免死循环,可以引入一个变量作为“开关”。在实现数据获取目的的同时,利用该开关机制避免 DOM 的无限循环渲染。

由于绘制动图较为繁琐,请读者结合代码注释进行理解。


function App() {  const [count, setCount] = useState(0);  const [loading, setLoading] = useState(true); // loading作为开关  useEffect(() => {    if (loading) { // 注意这里只有当loading为true时才执行      setTimeout(() => {        setCount(count + 1);        setLoading(!loading); // 改变loading值      });    }  }, [loading]); // loading在这里作为dependence
  // 第一次DOM渲染完成后,loading为true,执行副作用函数,count值变为1,loading变为false,由于
  // 改变了state的值,会update,组件会再次render,但此时loading为false,不会执行setTimeout,
  // 避免了循环

  // 当点击Refresh刷新,loading由上一次的false变为了true,函数执行一次update
  // DOM更新完后执行useEffect,因为loading已经为true了,所以副作用函数可执行,count从1变为2,
  // loading又从true变为false,就这样交替进行。。。
  return (    <div>      <h3>{count}</h3>      <button        onClick={() => {          setLoading(true);        }}      >        Refresh      </button>    </div>  );}

关于副作用的清除

useEffect 可以返回一个函数,该函数用于清除副作用。


  useEffect(() => {    
    ChatAPI.subscribeToFriendStatus(props.id, handleStatusChange);  
    function clear(){
        ChatAPI.unsubscribeFromFriendStatus(props.id, handleStatusChange);
    }  
    return clear;
});

这涉及到 useEffect 的执行与销毁过程,具体步骤如下:

  1. 传入 props.id = 1
  2. 组件渲染
  3. DOM 渲染完成,执行副作用函数,返回清除副作用函数 clear,命名为 clear1
  4. 传入 props.id=2
  5. 组件渲染
  6. DOM 渲染完成,执行 clear1
  7. 副作用函数执行并返回另一个 clear 函数,命名为 clear2
  8. 组件销毁,clear2 执行

由此可推断出副作用清除函数的几个特征:

  • 每次副作用执行都会返回一个清除函数
  • 清除函数会在下一次副作用函数执行之前(即 DOM 渲染完成之后)执行
  • 组件销毁时也会执行一次清除函数

从打印出的 count 值也可以看出,清除函数会在下一次副作用函数执行之前执行,即在清除函数里的 count 值是上一次缓存的 count 值:

避免重复循环渲染

进一步思考,clear1 执行的时候,访问了 props.id,那么这个 id 值是 1 还是 2 呢?

这里涉及闭包的概念。因为 useEffect 返回的是一个函数,在执行时产生了闭包。根据闭包定义,返回的 clear 函数能访问其自身作用域外的变量。当组件第一次渲染时传入 id=1,此时 clear 函数中的 props.id 值为 1。

以上就是 React useEffect 的理解与使用的详细内容,更多关于 React useEffect 的资料请关注本站其它相关文章!

作者最新文章
相关文章
更多