agentmentoragentmentor

第 06 节:Effects:和 React 外面的世界同步

本节 objectives:

  • 能判断一段逻辑是否真的需要 effect。
  • 能写出基础 useEffect 和依赖数组。
  • 能解释 cleanup 的用途。

先修:state、props 和条件渲染 | 上一节 << 05 | 下一节 07 >>

不是每段"之后要做的事"都叫 effect

React 初学者常把 useEffect 当万能工具:算派生值、响应点击、整理数据都塞进去。官方文档的定义更窄:effects 让组件和外部系统同步,比如浏览器 API、网络连接、第三方小组件或订阅。1

先问一句:这段代码是不是在和 React 外面的东西同步?如果不是,可能不需要 effect。

讲解

基础形状:

jsx
import { useEffect } from "react";
function PageTitle({ title }) {  useEffect(() => {    document.title = title;  }, [title]);
  return <h1>{title}</h1>;}

第一个参数是 effect 函数。第二个参数是依赖数组,告诉 React 这个 effect 依赖哪些值。这里 title 变化时,浏览器标签标题也要同步变化。1

如果 effect 建立了订阅、计时器或连接,通常要返回 cleanup:

jsx
useEffect(() => {  const id = setInterval(tick, 1000);  return () => clearInterval(id);}, []);

空依赖数组表示这段 effect 不依赖组件内会变化的值,通常在组件挂载后建立一次,卸载时清理一次。别把它理解成"逃避依赖检查"。

跟我做一遍(worked example)

需求:课程详情页打开时,把浏览器标签标题同步成课程名。

jsx
import { useEffect } from "react";
function CoursePage({ course }) {  useEffect(() => {    document.title = `${course.title} · Agent Mentor`;  }, [course.title]);
  return (    <main>      <h1>{course.title}</h1>      <p>{course.description}</p>    </main>  );}

为什么这是 effect:页面 <h1> 是 React 自己渲染的 UI,但 document.title 是浏览器外部状态。组件需要把 React state/props 同步到外部系统。

换你补全(faded example)

补全一个窗口宽度监听 effect。空白处决定"什么时候建立监听,什么时候清理"。

jsx
import { useEffect, useState } from "react";
function WindowWidth() {  const [width, setWidth] = useState(window.innerWidth);
  useEffect(() => {    function handleResize() {      setWidth(window.innerWidth);    }
    window.addEventListener("resize", handleResize);    return () => {      ______________________________;    };  }, ________);
  return <p>窗口宽度:{width}</p>;}

参考答案:

jsx
return () => {  window.removeEventListener("resize", handleResize);};}, []);

这是一个外部浏览器事件订阅。cleanup 避免组件卸载后还留下监听器。

小结 + 通向下一节

Effect 是同步边界,不是所有逻辑的垃圾桶。先用 props、state、事件和渲染表达清楚 UI;只有碰到外部系统时再拿出 effect。

最后一节把输入框、state、列表和事件合起来,做一个小型受控表单组件。

Footnotes

  1. Synchronizing with Effects — https://react.dev/learn/synchronizing-with-effects 2

练习

Level 1: 写一个 TitleSync 组件,接收 title props,用 effect 同步 document.title

提示 1

先确认这是外部系统:浏览器网络状态。

提示 2

注册什么,就清理什么。

提示 3

依赖数组不是装饰,要和 effect 用到的变化值对齐。

自评