React 19 入门教程
useEffect 心智模型
本教程共 50 篇 · 第 27 篇 · 更新于 2026-07-29 · 约 8 分钟阅读
ReactuseEffect副作用依赖数组同步
27. useEffect 心智模型
本节目标:建立 useEffect 的正确心智模型,理解渲染与副作用的区别,掌握依赖数组的用法。
两种代码:渲染逻辑 vs 副作用
React 组件里有两种代码:
渲染逻辑(纯函数,位于组件顶层):
function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');
// 根据 props 和 state 计算 JSX
return <div>{message}</div>;
}
渲染逻辑必须是纯的——同样的输入永远返回同样的 JSX,不能修改外部世界。
副作用(与外部系统同步,位于 useEffect 内):
useEffect(() => {
// 连接服务器、订阅事件、操作 DOM
const connection = createConnection(roomId);
connection.connect();
}, [roomId]);
副作用就是”跳出 React 做的事”——连接网络、操作 DOM、订阅事件。
为什么叫”副作用”
在函数式编程里,纯函数只做计算。任何修改外部世界的行为都叫”副作用”。Effect 就是 React 管理副作用的机制。
useEffect 的基本结构
useEffect(() => {
// effect 主体:每次同步时执行
return () => {
// 清理函数:每次重新同步前执行
};
}, [依赖数组]);
三个部分:
- 主体:同步到最新状态
- 清理函数:停止旧同步
- 依赖数组:控制何时重新同步
依赖数组决定什么时候跑
// 没有依赖数组:每次渲染后都跑
useEffect(() => {
console.log('每次渲染都执行');
});
// 空依赖数组:只跑一次(挂载时)
useEffect(() => {
console.log('只在首次渲染执行');
}, []);
// 有依赖:依赖变化时才跑
useEffect(() => {
console.log(`房间切换到 ${roomId}`);
}, [roomId]);
依赖不能省略
effect 里用的所有响应式值(props、state、组件内变量)都要写进依赖数组。否则 effect 读到的是过期值。
一个聊天室的例子
function ChatRoom({ roomId }) {
useEffect(() => {
const connection = createConnection(roomId);
connection.connect();
console.log(`连接到 ${roomId}`);
return () => {
connection.disconnect();
console.log(`断开 ${roomId}`);
};
}, [roomId]);
return <h1>当前房间:{roomId}</h1>;
}
切换 roomId 时:
- React 用新 roomId 渲染
- 执行清理函数:断开旧房间
- 执行 effect 主体:连接新房间
effect 不是生命周期
新手容易把 useEffect 类比为 componentDidMount、componentDidUpdate。这是错的。
组件生命周期:挂载 → 更新 → 卸载
effect 生命周期:开始同步 → 停止同步 → 开始同步 → …
effect 的关注点是”同步”,不是”生命周期阶段”。同一个 effect 可能执行多次。
// 错误心智模型
useEffect(() => {
// 这不只是"挂载时"执行,依赖变化也会执行
}, []);
// 正确心智模型
useEffect(() => {
// 开始同步到 roomId
return () => {
// 停止同步到旧的 roomId
};
}, [roomId]);
effect 的执行时机
React 在浏览器绘制之后才执行 effect。这意味着:
- 组件函数执行(渲染)
- React 更新 DOM
- 浏览器绘制页面
- React 执行 effect
所以 effect 不会阻塞页面渲染。
为什么是绘制之后
如果 effect 在绘制之前执行,用户可能看到闪烁。React 确保用户先看到更新后的页面,再执行副作用。
实战:订阅和取消订阅
function OnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function handleOnline() { setIsOnline(true); }
function handleOffline() { setIsOnline(false); }
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []); // 空依赖:只订阅一次
return isOnline ? '在线' : '离线';
}
实战:根据 props 获取数据
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
setUser(data);
setLoading(false);
});
}, [userId]); // userId 变化时重新获取
if (loading) return <p>加载中...</p>;
return <p>{user.name}</p>;
}
常见心智模型错误
| 错误认知 | 正确认知 |
|---|---|
| useEffect 是生命周期 | useEffect 是同步机制 |
| 依赖数组控制”是否执行” | 依赖数组控制”何时重新同步” |
| 清理函数只在卸载时跑 | 清理函数在每次重新同步前跑 |
| effect 在渲染前执行 | effect 在浏览器绘制后执行 |
本节要点
- 渲染逻辑是纯的,副作用在 useEffect 里
- 依赖数组控制 effect 何时重新同步
- 清理函数在重新同步前执行
- effect 是同步机制,不是生命周期