[ PROMPT_NODE_25248 ]
rerender-functional-setstate
[ SKILL_DOCUMENTATION ]
## 使用函数式 setState 更新
当基于当前状态值更新状态时,请使用 setState 的函数式更新形式,而不是直接引用状态变量。这可以防止闭包过时(stale closures),消除不必要的依赖项,并创建稳定的回调引用。
**错误做法(需要将 state 作为依赖项):**
tsx
function TodoList() {
const [items, setItems] = useState(initialItems)
// 回调必须依赖 items,在每次 items 更改时都会重新创建
const addItems = useCallback((newItems: Item[]) => {
setItems([...items, ...newItems])
}, [items]) // ❌ items 依赖导致重复创建
// 如果忘记依赖项,存在闭包过时的风险
const removeItem = useCallback((id: string) => {
setItems(items.filter(item => item.id !== id))
}, []) // ❌ 缺少 items 依赖 - 将使用过时的 items!
return
}
第一个回调在 `items` 每次更改时都会重新创建,这可能导致子组件不必要地重新渲染。第二个回调存在闭包过时错误——它将始终引用初始的 `items` 值。
**正确做法(稳定的回调,无闭包过时风险):**
tsx
function TodoList() {
const [items, setItems] = useState(initialItems)
// 稳定的回调,从不重新创建
const addItems = useCallback((newItems: Item[]) => {
setItems(curr => [...curr, ...newItems])
}, []) // ✅ 无需依赖项
// 始终使用最新状态,无闭包过时风险
const removeItem = useCallback((id: string) => {
setItems(curr => curr.filter(item => item.id !== id))
}, []) // ✅ 安全且稳定
return
}
**优势:**
1. **稳定的回调引用** - 状态更改时无需重新创建回调
2. **无闭包过时** - 始终操作最新的状态值
3. **更少的依赖项** - 简化依赖数组并减少内存泄漏
4. **防止 Bug** - 消除了 React 中最常见的闭包 Bug 来源
**何时使用函数式更新:**
- 任何依赖于当前状态值的 setState
- 在需要状态的 useCallback/useMemo 内部
- 引用状态的事件处理程序
- 更新状态的异步操作
**何时可以直接更新:**
- 将状态设置为静态值:`setCount(0)`
- 仅根据 props/参数设置状态:`setName(newName)`
- 状态不依赖于先前的值