[ PROMPT_NODE_25292 ]
alternatives
[ SKILL_DOCUMENTATION ]
# useEffect 的更好替代方案
## 1. 在渲染期间计算 (派生状态)
对于从 props 或 state 派生的值,直接计算即可:
tsx
function Form() {
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
// 每次渲染都会运行 - 这是正常且刻意的
const fullName = firstName + ' ' + lastName;
const isValid = firstName.length > 0 && lastName.length > 0;
}
**何时使用**:当值可以从现有的 props/state 计算得出时。
---
## 2. 使用 useMemo 处理昂贵计算
当计算量很大时,使用 memoize 缓存:
tsx
import { useMemo } from 'react';
function TodoList({ todos, filter }) {
const visibleTodos = useMemo(
() => getFilteredTodos(todos, filter),
[todos, filter]
);
}
**如何判断是否昂贵**:
tsx
console.time('filter');
const visibleTodos = getFilteredTodos(todos, filter);
console.timeEnd('filter');
// 如果 > 1ms,考虑使用 memoize
**注意**:React 编译器可以自动进行 memoize,减少手动 useMemo 的需求。
---
## 3. 使用 Key 属性重置状态
当 prop 改变时重置所有状态,使用 key:
tsx
// 父组件传递 userId 作为 key
function ProfilePage({ userId }) {
return (
);
}
function Profile({ userId }) {
// 当 userId 改变时,这里的所有状态都会重置
const [comment, setComment] = useState('');
const [likes, setLikes] = useState([]);
}
**何时使用**:当你希望在标识性 prop 改变时获得“全新开始”时。
---
## 4. 存储 ID 而非对象
在列表改变时保持选中状态:
tsx
// 坏做法:存储对象,需要 Effect 来“调整”
function List({ items }) {
const [selection, setSelection] = useState(null);
useEffect(() => {
setSelection(null); // items 改变时重置
}, [items]);
}
// 好做法:存储 ID,派生出对象
function List({ items }) {
const [selectedId, setSelectedId] = useState(null);
// 派生 - 无需副作用
const selection = items.find(item => item.id === selectedId) ?? null;
}
**优势**:如果新列表中存在具有 selectedId 的项,选中状态会被保留。
---
## 5. 用户操作使用事件处理器
用户的点击/提交/拖拽应在事件处理器中处理,而非副作用:
tsx
// 事件处理器确切知道发生了什么
function ProductPage({ product, addToCart }) {
function handleBuyClick() {
addToCart(product);
showNotificat