今天我们聚焦一个让人又爱又怕的 Hook —— useEffect
,它是副作用的管理器,是函数组件的“隐藏执行者”。
我们将从基础语法,到执行机制,再到真实开发场景,完整拆解 useEffect
的使用逻辑与工作原理。
useEffect
?很多人第一次用 useEffect
,就遇到这些问题:
🌀 “为什么副作用执行了两次?” 😱 “组件卸载时怎么还在请求接口?” 💣 “useEffect 写在按钮里怎么报错了?”
这些其实都跟它的规则和生命周期时机有关。
我们来带着这些问题,逐个拆解。
📌 通俗解释:
副作用 = 不直接参与 UI 渲染、但“影响了组件生命周期”的逻辑行为
📦 常见副作用包括:
👀 可以类比成做饭时顺手擦桌子 —— 主任务是做饭,擦桌子是副任务,但它依然需要安排好时机执行。
useEffect
的标准写法如下:
useEffect(() => {
// 副作用代码
}, [依赖项]);
解释:
📌 不同依赖写法 = 不同执行时机:
写法 | 执行时机说明 |
---|---|
无依赖项 | 每次渲染后都会执行 |
空数组 [] | 仅在挂载时执行一次(相当于 componentDidMount) |
有依赖项 [search] | 仅当指定依赖项发生变化时才重新执行副作用 |
代码示例:
// 每次渲染后都执行
useEffect(() => {
console.log("组件更新了");
});
// 指定依赖项
useEffect(() => {
console.log("search 变化了");
}, [search]);
// 只在挂载时执行一次
useEffect(() => {
console.log("组件加载了");
}, []);
✅ 推荐每次写 useEffect
都明确依赖数组,避免无意识触发。
useEffect
的两条硬性规则🧱 Hooks 规则必须记牢
:
错误示例:
export function BadComponent() {
function handleClick() {
// ❌ 错误:在事件处理函数中调用 Hook
useEffect(() => {
console.log("点击副作用");
});
}
return <button onClick={handleClick}>Click</button>;
}
生命周期时机 | useEffect 行为 |
---|---|
初次渲染完成 | 执行副作用函数 |
依赖项变化(非空) | 清除上一次副作用 → 执行新的副作用函数 |
组件卸载 | 清除副作用 |
📌 精髓:
清理函数是
useEffect
返回的函数,它会在副作用重跑前或组件卸载时被调用
常见用途:
代码示例:
useEffect(() => {
const handler = () => console.log("点击");
document.addEventListener("click", handler);
return () => {
document.removeEventListener("click", handler); // 清理
};
}, []);
🧼 类比生活:副作用是吃完饭,清理函数就是洗碗 ✨
👨💻 获取后端数据,是 useEffect
最典型的用法之一。
export function PostsPage() {
const [posts, setPosts] = useState<PostData[]>([]);
useEffect(() => {
getPosts().then((data) => {
setPosts(data);
});
}, []);
}
📌 Tips:
useEffect
外层用 async!useEffect(() => {
async function fetchData() {
const res = await fetch('/api/posts');
const data = await res.json();
setPosts(data);
}
fetchData(); // 内部调用 async 函数
}, []);
✅ 推荐增加 isLoading
和 error
状态来更好管理异步流程:
export function PostsPage() {
const [isLoading, setIsLoading] = useState(true);
const [posts, setPosts] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
let ignore = false;
async function fetchData() {
try {
const res = await fetch('/api/posts');
const data = await res.json();
if (!ignore) setPosts(data);
} catch (err) {
if (!ignore) setError(err);
} finally {
if (!ignore) setIsLoading(false);
}
}
fetchData();
// 清理副作用
return () => {
ignore = true;
};
}, []);
}
📌 说明:
ignore
flag 防止组件卸载后仍更新状态AbortController
中断请求⚠️ 在开发模式中,React 18 严格模式(StrictMode)下会故意模拟卸载 + 重建
✅ 关键词 | 📌 要点说明 |
---|---|
副作用(Side Effect) | 组件渲染之外的逻辑:获取数据、DOM 操作、订阅等 |
useEffect() | 处理副作用的 Hook,配合依赖项控制执行时机 |
清理函数 | 返回值函数:在下一次执行/卸载前清除副作用 |
异步请求最佳实践 | 使用 async/await + 错误处理 + loading 状态管理 |
Hook 使用规则 | 只能顶层调用,不能用于 class 组件、if/for 结构中 |
👋 喜欢这一期的朋友别忘了:
点赞、转发、收藏!有任何问题也欢迎在评论区留言,我会逐条解答!
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有