如何给传给子组件的setState添加重载并保留获取prevState的能力?
解决封装setState时兼容函数式更新的问题
你的核心问题是封装的$setIsOpen没有兼容React原生更新函数支持的函数式参数。原生useState返回的更新函数可以接受两种输入:直接的新状态值,或者接收上一状态并返回新状态的函数。要实现完全兼容,需要在封装函数中区分参数类型并做对应处理。
JavaScript 实现
import { useState } from 'react'; function Child({ isOpen, setIsOpen }) { return ( <div> <button onClick={() => setIsOpen(true)}>设为开启</button> <button onClick={() => setIsOpen(prev => !prev)}>切换状态</button> <p>当前状态:{isOpen ? '已开启' : '已关闭'}</p> </div> ); } function Parent() { const [isOpen, setIsOpen] = useState(false); const $setIsOpen = (update) => { // 执行你的额外操作,比如日志、埋点等 console.log("触发状态更新,执行额外操作"); // 区分参数是值还是函数式更新 if (typeof update === 'function') { // 传入函数时,委托给原生setIsOpen,同时可在内部基于新状态做操作 setIsOpen(prevState => { const newState = update(prevState); console.log(`状态更新完成:${prevState} → ${newState}`); return newState; }); } else { // 传入值时直接传递,并执行额外操作 console.log(`状态更新完成:${isOpen} → ${update}`); setIsOpen(update); } }; return <Child isOpen={isOpen} setIsOpen={$setIsOpen} />; } export default Parent;
TypeScript 实现
TypeScript中可以直接复用React内置的Dispatch<SetStateAction<T>>类型,确保封装函数的类型和原生更新函数完全一致:
import { useState, Dispatch, SetStateAction } from 'react'; // 定义子组件Props类型 type ChildProps = { isOpen: boolean; setIsOpen: Dispatch<SetStateAction<boolean>>; }; function Child({ isOpen, setIsOpen }: ChildProps) { return ( <div> <button onClick={() => setIsOpen(true)}>设为开启</button> <button onClick={() => setIsOpen(prev => !prev)}>切换状态</button> <p>当前状态:{isOpen ? '已开启' : '已关闭'}</p> </div> ); } function Parent() { const [isOpen, setIsOpen] = useState(false); // 直接使用React内置类型,保证类型兼容 const $setIsOpen: Dispatch<SetStateAction<boolean>> = (update) => { // 执行额外操作 console.log("触发状态更新,执行额外操作"); if (typeof update === 'function') { setIsOpen(prevState => { const newState = update(prevState); console.log(`状态更新完成:${prevState} → ${newState}`); return newState; }); } else { console.log(`状态更新完成:${isOpen} → ${update}`); setIsOpen(update); } }; return <Child isOpen={isOpen} setIsOpen={$setIsOpen} />; } export default Parent;
关键说明
- 当传入函数式更新参数时,必须把这个函数传递给原生
setIsOpen,由React在合适时机调用它并传入最新的prevState,不能自己提前执行(否则会拿到过时的状态)。 - 如果你的额外操作不需要依赖新状态,可以简化代码,直接把
update参数传递给原生setIsOpen,不需要区分类型:
这种写法也能兼容两种更新方式,只是无法在额外操作中获取新状态值。const $setIsOpen = (update) => { console.log("执行额外操作"); setIsOpen(update); };
内容的提问来源于stack exchange,提问作者Yoel
相关产品推荐
相关产品推荐

