mcpField
R
rerender-functional-setstate

rerender-functional-setstate

github.com/theorcdev开发2.1k35 次安装2 天前更新
代码仓库

关于 rerender-functional-setstate

面向 React 开发者的编码技能,建议在状态更新依赖当前值时使用函数式 `setState`。它说明这种写法如何避免过期闭包,并减少依赖、保持回调稳定。

主要功能

  1. 01基于最新状态值更新
  2. 02避免过期闭包问题
  3. 03保持回调稳定
  4. 04减少 Hook 依赖项

适用场景

  1. 01向 state 数组追加项目
  2. 02在回调中更新状态
  3. 03在事件处理程序或异步操作中修改状态

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/theorcdev/8bitcn-ui --skill rerender-functional-setstate

安装后在 Claude Code 中输入/rerender-functional-setstate确认已加载。

详细介绍

使用函数式 setState 更新

根据当前状态值更新状态时,请使用 setState 的函数式更新形式,而不是直接引用状态变量。这样可以避免闭包过期、消除不必要的依赖,并创建稳定的回调引用。

错误(需要将状态作为依赖项):

function TodoList() {
  const [items, setItems] = useState(initialItems)

  // Callback must depend on items, recreated on every items change
  const addItems = useCallback((newItems: Item[]) => {
    setItems([...items, ...newItems])
  }, [items])  // items dependency causes recreations

  // Risk of stale closure if dependency is forgotten
  const removeItem = useCallback((id: string) => {
    setItems(items.filter(item => item.id !== id))
  }, [])  // Missing items dependency - will use stale items!

  return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} />
}

第一个回调会在 items 每次变化时重新创建,这可能导致子组件不必要地重新渲染。第二个回调存在闭包过期问题——它始终会引用初始的 items 值。

正确(回调稳定,不会出现闭包过期):

function TodoList() {
  const [items, setItems] = useState(initialItems)

  // Stable callback, never recreated
  const addItems = useCallback((newItems: Item[]) => {
    setItems(curr => [...curr, ...newItems])
  }, [])  // No dependencies needed

  // Always uses latest state, no stale closure risk
  const removeItem = useCallback((id: string) => {
    setItems(curr => curr.filter(item => item.id !== id))
  }, [])  // Safe and stable

  return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} />
}

优点:

  1. 回调引用稳定 - 状态变化时无需重新创建回调
  2. 不会出现闭包过期 - 始终基于最新的状态值进行操作
  3. 依赖项更少 - 简化依赖数组并减少内存泄漏
  4. 防止错误 - 消除最常见的 React 闭包错误来源

适合使用函数式更新的情况:

  • 任何依赖当前状态值的 setState
  • 在需要使用状态的 useCallback/useMemo 中
  • 引用了状态的事件处理程序
  • 更新状态的异步操作

适合直接更新的情况:

  • 将状态设为静态值:setCount(0)
  • 仅根据 props/参数设置状态:setName(newName)
  • 状态不依赖之前的值

**注意:**如果你的项目启用了 React Compiler,编译器可以自动优化某些情况,但仍建议使用函数式更新,以确保正确性并防止闭包过期错误。

评论

支持 Markdown