React 19 入门教程
你可能不需要 Effect
本教程共 50 篇 · 第 29 篇 · 更新于 2026-07-29 · 约 7 分钟阅读
ReactuseEffect派生状态状态重置最佳实践
29. 你可能不需要 Effect
本节目标:识别不必要的 effect,学会用更简单的方式替代,让代码更简洁、更不容易出错。
Effect 是”脱围机制”
React 官方文档里有一句很精辟的话:Effect 是 React 范式的一种脱围机制。
意思是:正常的 React 流程是 props → state → UI。当你需要”跳出”这个流程,与外部系统交互时,才用 Effect。
如果你的 effect 只是在 React 内部打转(根据 state 更新 state),大概率不需要。
情况一:能算就别存
根据现有 state 算出来的值,不要存 state,更不要用 effect 更新:
// 不好:多余的 state + effect
function Form() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
return <p>{fullName}</p>;
}
// 好:直接计算
function Form() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const fullName = firstName + ' ' + lastName;
return <p>{fullName}</p>;
}
判断标准
如果一个值能从 props 或 state 算出来,它就不应该是 state。
情况二:转换渲染数据
渲染前要筛选/排序列表,在组件顶层直接算:
// 不好:用 effect 存筛选结果
function TodoList({ todos, filter }) {
const [visibleTodos, setVisibleTodos] = useState([]);
useEffect(() => {
setVisibleTodos(todos.filter(t => t.status === filter));
}, [todos, filter]);
return <ul>{visibleTodos.map(...)}</ul>;
}
// 好:渲染时直接算
function TodoList({ todos, filter }) {
const visibleTodos = todos.filter(t => t.status === filter);
return <ul>{visibleTodos.map(...)}</ul>;
}
如果筛选计算很贵,用 useMemo 缓存结果,但也不是 effect。
情况三:props 变化时重置 state
用 effect 根据 props 重置 state,不如用 key:
// 不好:用 effect 重置
function ProfilePage({ userId }) {
const [comment, setComment] = useState('');
useEffect(() => {
setComment(''); // userId 变化时重置
}, [userId]);
}
// 好:用 key 重置
function ProfilePage({ userId }) {
return <Profile userId={userId} key={userId} />;
}
function Profile({ userId }) {
const [comment, setComment] = useState('');
// userId 变化时,key 变化,组件自动重置
}
key 重置更简洁,而且能重置组件内所有 state,不用一个个写。
情况四:处理用户事件
用户事件(点击、提交)的处理逻辑放在事件处理函数里,不要放在 effect 里:
// 不好:用 effect 响应事件
function Cart() {
const [cart, setCart] = useState([]);
useEffect(() => {
if (cart.length > 0) {
post('/api/cart', { items: cart });
}
}, [cart]); // cart 变化就发请求
}
// 好:在点击事件里发请求
function Cart() {
const [cart, setCart] = useState([]);
function handleCheckout() {
post('/api/cart', { items: cart });
}
return <button onClick={handleCheckout}>结算</button>;
}
确实需要 Effect 的场景
以下场景才真正需要 effect:
- 连接/断开外部服务(WebSocket、聊天室)
- 订阅浏览器事件(resize、scroll、online)
- 操作第三方 DOM 库(地图、图表)
- 发送分析日志(组件曝光时)
- 同步到 localStorage
一个简单的判断
问自己:这个操作是在和”外部系统”交互吗?如果不是,大概率不需要 effect。
移除不必要的 Effect 清单
- 能算出来的值 → 直接计算,不存 state
- 渲染前转换数据 → 在组件顶层算
- props 变化重置 state → 用 key
- 响应用户操作 → 放在事件处理函数
- 初始化第三方库 → 可以,但要清理
- 订阅外部事件 → 可以,但要清理
一个综合例子
// 之前:到处都是 effect
function SearchBox({ defaultQuery }) {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
// 不必要的 effect:初始化
useEffect(() => {
setQuery(defaultQuery);
}, [defaultQuery]);
// 不必要的 effect:派生数据
useEffect(() => {
setResults(filterItems(query));
}, [query]);
return (/* ... */);
}
// 之后:干净多了
function SearchBox({ defaultQuery }) {
const [query, setQuery] = useState(defaultQuery);
const results = filterItems(query);
return (/* ... */);
}
本节要点
- Effect 是脱围机制,不是常规工具
- 能算出来的值不存 state
- props 变化重置 state 用 key
- 用户事件放在事件处理函数
- 真正需要 effect 的是与外部系统交互