在React中,副作用就是组件在渲染之外做的“额外的事情”,这些事情会影响到组件外部的世界。想象你在做饭(渲染组件),主要任务是把菜做好(生成界面)。但如果你在炒菜的同时,还顺手打开了电视、接了个电话、或者去敲邻居的门,这些“额外的事情”就是副作用,因为它们超出了“做菜”这个核心任务。
在React里,组件的主要任务是根据数据(状态或props)生成界面(JSX)。但有时候,组件还需要做一些“杂事”,比如:
- 从网上抓数据(比如请求API)。
- 设置一个定时器(比如每秒更新时间)。
- 直接改网页上的某个元素(比如改标题)。
- 监听用户的操作(比如监听窗口大小变化)。
这些“杂事”就是副作用,因为它们不是直接生成界面,而是影响了外部的东西(网络、浏览器、DOM等)
为什么React要特别管理副作用?
React希望组件的渲染(生成界面)是“纯净”的,就像一道数学公式:给相同的输入(状态/props),总是得到相同的输出(界面)。但副作用会让事情变得“杂乱”,因为它们可能会改变外部的东西,或者依赖外部的不可控因素(比如网络请求的结果)。所以,React用useEffect把这些“杂事”隔离起来,单独处理。
举个生活中的例子
假设你在画一幅画(类似React组件渲染):
- 主要任务:根据你的想法(状态),画出一幅漂亮的画(界面)。
- 副作用:你在画画的过程中,还顺便:
- 打电话问朋友意见(类似API请求)。
- 设置一个闹钟提醒自己休息(类似定时器)。
- 在画布旁边写字(类似操作DOM)。
这些“顺便”做的事就是副作用,它们跟画画本身无关,但可能会影响外界(比如朋友、闹钟、画布外的区域)。
用React代码简单看一下
假设你写了个React组件,显示一个计数器:
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>计数: {count}</p>
<button onClick={() => setCount(count + 1)}>加1</button>
</div>
);
}
这里,组件的主要任务是显示count和一个按钮,渲染出界面。这是“画画”的部分。
现在,如果你想在组件加载时从服务器获取初始的count值,或者每秒自动加1,这些就是副作用。因为它们:
- 涉及外部系统(服务器、定时器)。
- 不是直接生成界面的工作。
用useEffect处理这些副作用:
import { useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
// 副作用:从服务器获取数据
useEffect(() => {
fetch('https://api.example.com/count')
.then(res => res.json())
.then(data => setCount(data.count));
}, []); // 空数组,只在组件第一次加载时运行
// 副作用:设置定时器每秒加1
useEffect(() => {
const timer = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(timer); // 清理定时器,防止内存泄漏
}, []);
return (
<div>
<p>计数: {count}</p>
<button onClick={() => setCount(count + 1)}>加1</button>
</div>
);
}
重点总结
- 副作用是“额外的事情”:组件渲染(生成界面)之外的、影响外部的操作,比如网络请求、定时器、DOM操作。
- 为什么隔离副作用:React希望渲染过程简单、可预测,副作用会让事情复杂,所以用useEffect单独管理。
- 怎么理解useEffect:它就像一个“杂事管家”,在组件渲染完后处理那些“额外的事情”,还能在需要时清理(比如取消定时器)。