搞定 React Hooks!useState+useEffect 超详攻略,新手也能秒懂🐱

135 阅读4分钟

🤯 刚学 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 条准则🍃

  1. useState 管 “数据记忆” :需要保存的变量(比如计数器、列表数据)都用它定义,更新必须用 setXxx
  2. useEffect 管 “额外操作” :数据请求、定时器、事件监听这些 “副作用”,全交给他处理。
  3. 依赖项别瞎写:空数组 [] 只跑一次,写状态就 “谁变跑谁”,漏写依赖会导致逻辑出错!