React Hooks特性之useEffect解析

前言

在 React 的函数式组件开发中,useEffect 是一个极其重要的 Hook。它让我们能够在函数组件中处理副作用操作,如数据获取、订阅、手动 DOM 操作等。

React Hooks 基础回顾

在深入 useEffect 之前,我们先简单回顾一下 React Hooks 的基本概念。Hooks 是 React 16.8 引入的新特性,允许我们在不编写 class 组件的情况下使用 state 和其他 React 特性。

useEffect 是 Hooks 中最核心的一个,用于处理副作用操作。

useEffect深度解析

类型 示例 是否属于副作用
纯函数 const sum = (a, b) => a + b ❌ 无副作用
副作用操作 fetch(), setTimeout() ✅ 有副作用
状态更新 setState() ✅ 有副作用

基本概念

useEffect是React专门用于处理副作用的Hook,它能够:

  • 在组件渲染后执行副作用操作
  • 控制副作用的执行时机
  • 提供清理机制避免内存泄漏

副作用类型分析

执行机制详解

useEffect的执行可以分为三个阶段:

  1. ​挂载阶段​

    • 组件首次渲染后执行
    • 类似于类组件的componentDidMount
  2. ​更新阶段​

    • 依赖项变化时重新执行
    • 类似于类组件的componentDidUpdate
  3. ​卸载阶段​

    • 组件卸载时执行清理函数
    • 类似于类组件的componentWillUnmount

实战应用模式

模式一:持续执行的副作用

当不需要控制执行时机时,可以省略依赖数组:

jsx 复制代码
useEffect(() => {
  console.log('每次渲染后都会执行');
});

模式二:单次执行的副作用

通过空依赖数组控制只在挂载时执行:

jsx 复制代码
useEffect(() => {
  console.log('仅在组件挂载时执行');
  return () => {
    console.log('组件卸载时执行清理');
  };
}, []);

模式三:条件执行的副作用

通过指定依赖项控制执行时机:

jsx 复制代码
const [count, setCount] = useState(0);

useEffect(() => {
  console.log(`count变为: ${count}`);
  document.title = `当前计数: ${count}`;
}, [count]);

模式四:需要清理的副作用

当副作用涉及资源管理时,必须提供清理函数:

jsx 复制代码
useEffect(() => {
  const timer = setInterval(() => {
    console.log('定时器运行中');
  }, 1000);
  
  return () => {
    clearInterval(timer);
  };
}, []);

综合例子解析

下面通过一个完整的组件示例,展示useEffect的四种使用模式:

jsx 复制代码
import React, { useState, useEffect } from 'react';

function EffectDemo() {
  const [count, setCount] = useState(0);
  const [data, setData] = useState('初始数据');
  const [width, setWidth] = useState(window.innerWidth);

  // 模式1: 持续执行
  useEffect(() => {
    console.log('【模式1】每次渲染后执行');
  });

  // 模式2: 单次执行
  useEffect(() => {
    console.log('【模式2】组件挂载时执行');
    return () => {
      console.log('【模式2】组件卸载时清理');
    };
  }, []);

  // 模式3: 条件执行
  useEffect(() => {
    console.log(`【模式3】count变为: ${count}`);
    document.title = `当前计数: ${count}`;
    return () => {
      console.log('【模式3】清理前一次effect');
    };
  }, [count]);

  // 模式4: 需要清理
  useEffect(() => {
    console.log('【模式4】设置窗口监听器');
    const handleResize = () => {
      setWidth(window.innerWidth);
      console.log('窗口大小变化:', window.innerWidth);
    };
    window.addEventListener('resize', handleResize);
    
    return () => {
      console.log('【模式4】移除窗口监听器');
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  return (
    <div style={{ padding: '20px', border: '1px solid #ccc' }}>
      <h2>useEffect四种模式示例</h2>
      {/* 各模式展示区域 */}
    </div>
  );
}

组件渲染流程分析

让我们分析一下组件在不同情况下的行为:

1. 组件首次挂载

  1. 执行所有useEffect钩子(按照声明顺序)

    • 模式1: 立即执行,控制台输出"【模式1】每次渲染后执行"
    • 模式2: 立即执行,控制台输出"【模式2】组件挂载时执行"
    • 模式3: 立即执行(因为count初始值变化被视为"变化"),控制台输出"【模式3】count变为: 0",设置文档标题
    • 模式4: 立即执行,控制台输出"【模式4】设置窗口监听器",添加resize事件监听器

2. 用户点击增加count按钮

  1. setCount触发状态更新

  2. 组件重新渲染

  3. 执行所有useEffect钩子:

  • 模式1: 再次执行,控制台输出"【模式1】每次渲染后执行"

  • 模式2: 不执行(依赖数组为空且不是首次渲染)

  • 模式3:

    先执行上一次effect的清理函数(如果有),控制台输出"【模式3】清理前一次effect",

    然后执行新的effect,控制台输出"【模式3】count变为: [新值]",更新文档标题。

  • 模式4: 不执行(依赖数组为空且不是首次渲染)

3. 调整浏览器窗口大小

  1. 浏览器触发resize事件

  2. 模式4中的handleResize函数被执行:

  • 更新width状态
  • 控制台输出"窗口大小变化: [新宽度]"
  1. 状态更新触发组件重新渲染

  2. 执行所有useEffect钩子:

  • 模式1: 再次执行,控制台输出"【模式1】每次渲染后执行"
  • 模式2: 不执行
  • 模式3: 不执行(count没有变化)
  • 模式4: 不执行(依赖数组为空且不是首次渲染)

4. 组件卸载

React调用所有effect的清理函数(按照与声明相反的顺序):

  • 模式4: 执行清理函数,控制台输出"【模式4】移除窗口监听器"
  • 模式3: 执行清理函数,控制台输出"【模式3】清理前一次effect"
  • 模式2: 执行清理函数,控制台输出"【模式2】组件卸载时清理"
  • 模式1: 没有清理函数,不执行任何操作
相关推荐
胡gh1 小时前
页面卡成PPT?重排重绘惹的祸!依旧性能优化
前端·javascript·面试
言兴2 小时前
# 深度解析 ECharts:从零到一构建企业级数据可视化看板
前端·javascript·面试
山有木兮木有枝_2 小时前
TailWind CSS
前端·css·postcss
烛阴3 小时前
TypeScript 的“读心术”:让类型在代码中“流动”起来
前端·javascript·typescript
杨荧3 小时前
基于Python的农作物病虫害防治网站 Python+Django+Vue.js
大数据·前端·vue.js·爬虫·python
Moment4 小时前
毕业一年了,分享一下我的四个开源项目!😊😊😊
前端·后端·开源
程序视点4 小时前
Escrcpy 3.0投屏控制软件使用教程:无线/有线连接+虚拟显示功能详解
前端·后端
silent_missile4 小时前
element-plus穿梭框transfer的调整
前端·javascript·vue.js
专注VB编程开发20年5 小时前
OpenXml、NPOI、EPPlus、Spire.Office组件对EXCEL ole对象附件的支持
前端·.net·excel·spire.office·npoi·openxml·spire.excel
古蓬莱掌管玉米的神5 小时前
coze娱乐ai换脸
前端