使用 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,直接在渲染里计算即可。
- 如果你只是响应用户操作(点击、输入),优先在事件处理函数里做事。
下一步
- 继续系统学习 API:useEffect 与 useEffect 参考。
- 想理解渲染与副作用分工:渲染和提交。
动手练习
运行并修改下面的组件,熟悉交互式示例的编辑与预览。
🌐浏览器运行