跳转到主要内容

使用 Effect 同步

组件渲染负责“算 UI”,但有些事情不属于 UI:更新浏览器标题、订阅 WebSocket、设置定时器、读写外部库…… 这些叫副作用(Side Effects)。React 用 Effect 来处理它们。

最小例子:同步 document.title

TypeScript
import { useEffect, useState } from 'react';

export default function TitleCounter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `点击次数:${count}`;
  }, [count]);

  return <button onClick={() => setCount((c) => c + 1)}>{count}</button>;
}

订阅与清理:房间切换时重新连接

TypeScript
import { useEffect, useState } from 'react';

type Connection = {
  connect: () => void;
  disconnect: () => void;
};

function createConnection(roomId: string): Connection {
  return {
    connect() {
      console.log('connect', roomId);
    },
    disconnect() {
      console.log('disconnect', roomId);
    },
  };
}

export default function ChatRoom() {
  const [roomId, setRoomId] = useState('general');

  useEffect(() => {
    const connection = createConnection(roomId);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);

  return (
    <>
      <select value={roomId} onChange={(e) => setRoomId(e.target.value)}>
        <option value="general">general</option>
        <option value="react">react</option>
        <option value="music">music</option>
      </select>
      <p>当前房间:{roomId}</p>
    </>
  );
}
常见坑
  • 依赖数组漏写:会用到“旧值”或造成不一致。
  • 依赖数组乱写:把每次渲染都会变化的对象/函数放进去,会导致 Effect 频繁重跑。
  • 忘了清理:订阅叠加、定时器越开越多、内存泄漏。

什么时候不需要 Effect?

经验法则
  • 如果你只是“根据 props/state 计算另一个值”,通常不需要 Effect,直接在渲染里计算即可。
  • 如果你只是响应用户操作(点击、输入),优先在事件处理函数里做事。

下一步

动手练习

运行并修改下面的组件,熟悉交互式示例的编辑与预览。

🌐浏览器运行
使用 Effect 同步 - React 文档 - React 文档