🤯 刚学 React 的小伙伴是不是都有过这种经历:看文档时觉得 useState 和 useEffect 好像不难,但一写代码就卡壳 —— 状态更新后页面没反应? useEffect 疯狂执行导致内存泄漏?
别慌!这篇文章就像你的 “专属导航”📚,用最接地气的例子拆解这两个核心 Hook,从基础用法到避坑技巧全涵盖,连刚入门的小白都能轻松跟上。读完直接上手写项目,告别 “一看就会,一写就废”的尴尬~ 往下滑,咱们马上发车🚀
React Hooks 是函数式组件的 “灵魂”,但很多新手刚上手时总被「状态管理」「副作用处理」搞得头大。今天就用最通俗的话 + 实战代码,带你吃透 useState 和 useEffect,看完直接上手写项目~✨
一、useState:状态管理的核心密码🎯
useState 是 React 给函数组件 “注入” 状态的工具,一句话总结:让组件拥有 “记忆”,能记住数据变化。
1.1 基础用法:定义 + 更新状态
const [count, setCount] = useState(0);
-
count:当前状态值(初始值是0) -
setCount:更新状态的函数(只能用它改count,不能直接赋值!)
比如点击按钮更新 count:
<button onClick={() => setCount(count + 1)}>点我+1</button>
👉 这里有个细节:更新状态后组件会重新渲染,页面上的 count 会自动显示最新值~
1.2 为什么要用函数式更新?
如果新状态依赖旧状态,推荐用「函数式更新」:
// 正确:确保拿到最新的 count
setCount(prevCount => prevCount + 1);
因为 React 状态更新是 “异步批量处理” 的,直接写 setCount(count + 1) 可能拿到旧值哦~😱
1.3 多状态管理:互不干扰
一个组件里可以定义多个 useState,彼此独立:
// App.jsx 中同时管理 count、num、repos
const [count, setCount] = useState(0);
const [num, setNum] = useState(0);
const [repos, setRepos] = useState([]); // 数组类型初始值
就像给组件装了多个 “小抽屉”,每个抽屉放不同数据,取用方便~📦
二、useEffect:副作用处理的万能钥匙🧹
组件渲染是 “正作用”,而数据请求、定时器、事件监听这些 “额外操作” 都是「副作用」,useEffect 就是专门管这些的!
2.1 模拟生命周期:3 种常用场景
useEffect 能模拟类组件的生命周期,全靠第二个参数「依赖项数组」:
🌱 挂载时执行(只跑一次)
依赖项传空数组 [],组件一渲染完就执行,适合初始化操作(比如请求接口):
// App.jsx 中请求 GitHub 仓库数据
useEffect(() => {
console.log("组件刚挂载完,只执行一次~");
const fetchRepos = async () => {
const res = await fetch("https://api.github.com/users/shunwuyu/repos");
const data = await res.json();
setRepos(data); // 存到 repos 状态里
};
fetchRepos();
}, []); // 空数组 = 只在挂载时执行
👉 为什么不在组件顶层直接请求?因为会阻塞渲染!useEffect 会等渲染完再执行,不卡页面~
🔄 更新时执行(依赖变化才跑)
依赖项传具体状态,只有状态变了才执行:
// 只有 count 变了才打印
useEffect(() => {
console.log("count 变了:", count);
}, [count]); // 依赖 count
如果依赖多个状态,就都放进数组:[count, num],任一变化就触发~
🧹 卸载时清理(避免内存泄漏)
组件卸载前,有些操作必须 “善后”(比如清定时器、取消请求),这时候用 useEffect 的返回值:
// Timer 组件中清理定时器
useEffect(() => {
const timer = setInterval(() => {
setTime(prev => prev + 1);
}, 1000);
// 卸载时执行的清理函数
return () => {
clearInterval(timer); // 清掉定时器,防止内存泄漏
console.log("Timer 组件卸载啦~");
};
}, []);
👉 比如点击 “toggle timer” 按钮隐藏 Timer 时,这个清理函数就会触发,超安心~
2.2 坑点预警:别直接写 async!🚨
useEffect 的回调函数不能直接是 async,会报错!正确写法是在内部定义 async 函数:
// 错误示例 ❌
useEffect(async () => {
const res = await fetch("xxx"); // 会报错!
}, []);
// 正确示例 ✅
useEffect(() => {
const fetchData = async () => { // 内部定义 async 函数
const res = await fetch("xxx");
};
fetchData(); // 调用它
}, []);
三、实战总结:记住这 3 条准则🍃
- useState 管 “数据记忆” :需要保存的变量(比如计数器、列表数据)都用它定义,更新必须用
setXxx。 - useEffect 管 “额外操作” :数据请求、定时器、事件监听这些 “副作用”,全交给他处理。
- 依赖项别瞎写:空数组
[]只跑一次,写状态就 “谁变跑谁”,漏写依赖会导致逻辑出错!