CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
React 核心/课时/useEffect
RawBlame
课时目录14 节

React 核心

  1. 01React 是什么
  2. 02JSX 与渲染
  3. 03函数组件
  4. 04Props 与 State
  5. 05事件处理
  6. 06条件渲染与列表
  7. 07useEffect
  8. 08useRef 与 DOM
  9. 09useMemo、useCallback 与自定义 Hook
  10. 10表单与受控组件
  11. 11Context 共享状态
  12. 12React Router 入门
  13. 13数据获取与错误边界
  14. 14综合练习与 Next.js 方向

useEffect

副作用、依赖数组、清理函数、fetch 数据。

35 分钟

useEffect

这一课你将能做到

  1. 说出 Effect 适合做什么、不适合做什么
  2. 写出带清理函数的 useEffect(避免组件卸了还在 setState)
  3. 看懂依赖数组 [] / [id] 的含义

为什么要学

有些事发生在「画完界面之后」:改 document.title、订阅窗口大小、按课程 slug 去拉进度。
这些叫与外部系统同步,用 useEffect。

人话:React 负责画界面;世界(浏览器、后端、定时器)不在 JSX 里,要另开一个「画完再做、走时再收尾」的通道。

常见误用:用 Effect 把 props 再抄一份到 state,结果多跑很多遍、输入光标乱跳。能在渲染时算出来的,不要 Effect。

先建立图像

组件渲染到屏幕
    ↓
执行 Effect
    ↓
依赖变了 → 先 cleanup(清理)→ 再跑新的 Effect
组件卸载 → cleanup

依赖数组是「什么变了才值得再同步一次」。

跟着做

步骤 1:改标题(最小外部同步)

import { useEffect, useState } from "react";

export function TitleDemo() {
  const [name, setName] = useState("同学");

  useEffect(() => {
    document.title = "你好," + name;
  }, [name]); // name 变了才改标题

  return (
    <input value={name} onChange={(e) => setName(e.target.value)} />
  );
}

document.title 不是 React 树的一部分,所以用 Effect。

步骤 2:请求 + 清理(本课最重要)

用户从课 A 很快切到课 B:A 的响应可能后到,把 B 的标题盖掉。要用旗标或 AbortController。

import { useEffect, useState } from "react";

export function TrackTitle({ slug }) {
  const [title, setTitle] = useState("");

  useEffect(() => {
    let cancelled = false;

    async function load() {
      const res = await fetch("/api/tracks/" + slug);
      const data = await res.json();
      if (!cancelled) setTitle(data.title);
    }

    load();
    return () => {
      cancelled = true; // 已换 slug 或卸载:别再 setState
    };
  }, [slug]);

  return <h1>{title || "加载中…"}</h1>;
}

人话:请求回来时,人可能已经走了。

步骤 3:空依赖 = 挂载后跑(开发模式可能两次)

useEffect(() => {
  console.log("挂载后同步一次");
  return () => console.log("卸载前清理");
}, []);

React 严格模式在开发期会故意重复挂载,帮你发现漏清理。生产不会这样双重调用副作用。看到两次 log 先别慌。

步骤 4:订阅要对上取消订阅

useEffect(() => {
  function onResize() {
    // 读窗口宽度等
  }
  window.addEventListener("resize", onResize);
  return () => window.removeEventListener("resize", onResize);
}, []);

只加不减会泄漏监听。定时器同样:clearInterval 配对。

步骤 5:清理订阅

useEffect(() => {
  const id = setInterval(() => console.log("tick"), 1000);
  return () => clearInterval(id);
}, []);

离开页面应停止 tick;否则泄漏。fetch 用 AbortController 同理。

讲清楚要点

  1. 能在渲染时计算的,不要 Effect(例如把 first+last 拼成 fullName)。
  2. 依赖数组要诚实:用到的 props/state 一般要写进去。漏了会「闭包旧值」:看起来用 id,实际一直请求第一门课。
  3. 清理订阅:addEventListener 配对 removeEventListener。
  4. 不要用 Effect 响应点击:点击逻辑放事件处理函数。
  5. Next.js 里服务端取数可少写这类样板,但「过期响应 / 清理」思想相同。

你可能会懵的地方

  • 开发模式跑两次?
    严格模式故意如此。清理函数写对了就没事。

  • eslint 的 exhaustive-deps
    听它。把函数放进 Effect 内,或包 useCallback,不要随手注释掉规则。

  • 把 props 同步进 state
    多数是多余。直接渲染 props;要「可改副本」时再设计(受控/非受控)。

  • 无限循环
    Effect 里 setState → 渲染 → 依赖每次都是新对象 → 再 Effect。依赖写对象字面量最容易中招。

  • 依赖数组漏了?
    可能用到过期闭包。加依赖或证明刻意忽略并注释原因。

错法对照

错法问题正解
props→state 无脑同步多余渲染/打架直接用 props
无 cleanup 的请求旧响应写回新页cancelled / AbortController
在 Effect 里处理按钮逻辑绕、难读onClick
依赖写 [] 却用了 id永远拿第一次[id]
无依赖却 setState 且无条件死循环看清依赖与更新条件
用 Effect 同步「可由 props 算出的值」多余渲染直接在渲染中计算

自测三问

  1. Effect 的清理函数什么时候跑?
  2. [id] 和 [] 差别?漏写 id 会怎样?
  3. 举一个不该用 Effect 的例子(能渲染时算出来的)。

小结一句话

Effect 用来同步外部世界;写清依赖,返回清理函数,别拿它当万能生命周期筐。

动手加深

写:挂载拉课表 → loading → setRows;卸载 abort。对照 react.dev Effect 文档「你可能不需要 Effect」。

跟着做(加厚 · react-effects)

步骤:最小可运行对照

在 Vite 或 Next 练习页粘贴本课核心示例,改一个输入,确认屏幕变化符合预期。再故意写一个错法表里的写法,看控制台/界面如何坏,然后改回。

每个概念都问:state 在谁手里?UI 哪一块会因它重绘?

操作顺序:打开对应文件 → 改一处可观察点(文案、类型、className 或分支)→ 保存看结果 → 复原或留下有意义的小改动。

步骤:错法故意练一次

先按「用 jQuery 式直接改 DOM 冒充完成」做 30 秒(或在注释里写出若这样做会怎样),再改成「改数据(state/props)让 UI 推导出来」。 目的不是留坑,而是让失败现象进长期记忆。

你可能会懵的地方(加厚)

  • 热更新后状态丢了?
    正常:整树重挂会重置 state。用 key 或外提状态保留;开发期别被吓到。

  • 和文档示例不一致?
    以你锁的 React 大版本 + react.dev 为准;旧博客的 class 组件示例仅作历史阅读。

  • 这课和前后课怎么接?
    回看大纲上一课的小结一句、下一课标题;用自己的话写「本课产出什么可观察结果」。写不出来就重做跟着做。

错法对照(加厚)

错法问题正解
用 jQuery 式直接改 DOM 冒充完成假完成/埋雷改数据(state/props)让 UI 推导出来
只看不敲第二天全忘至少跑通本课步骤
跳过自测三问以为会了闭卷答再对稿

延伸

  • react.dev Synchronizing with Effects
  • 下一课:useRef——不触发渲染的「盒子」。
加载本节练习…
← 上一课条件渲染与列表下一课 →useRef 与 DOM