React状态中存储自定义类实例是否合理?跨页表格选择实现疑问
你的实现存在的潜在问题
你当前的实现虽然能正常运行,但确实不符合React的状态设计最佳实践,存在几个容易被忽略的风险:
- 渲染更新问题:React的状态更新默认做浅比较,如果你的
Selections类的toggle方法是修改原有实例后返回this,那setState传入的对象和原state引用完全相同,React会认为状态没有变化,后续如果用到React.memo、PureComponent或者浅比较的性能优化逻辑,会出现状态修改后组件不重新渲染的bug,你现在没遇到问题大概率是其他状态更新带动了重渲染。 - 不符合可序列化要求:React官方明确推荐state中仅存储可序列化为JSON的纯数据,类实例包含方法、可能存在循环引用,后续如果要做状态持久化、服务端渲染、时间旅行调试,都会遇到无法序列化的问题,也没法直接在React DevTools中直观查看所有状态值。
- 可变状态风险:类实例的内部属性可以被任意方法修改,会打破React状态的可预测性,出现问题时很难追溯状态变更的链路。
复用与可测试性的替代方案
你提到的跨组件复用、单独单元测试的收益,完全可以用更符合React生态的方式实现,不需要把类实例存在state里:
- 纯函数工具集方案(更推荐)
把选择逻辑抽成独立的纯函数,state中只存储纯数据(比如你用到的Map对象),所有操作都接收旧状态返回新状态:
// 可单独测试、多组件复用 const SelectionUtils = { toggle(prevMap, data) { const newMap = new Map(prevMap) // 处理选择逻辑 return newMap }, add(prevMap, data) {/* 逻辑 */}, clear(prevMap) {/* 逻辑 */} } // 组件state仅存纯数据 this.state = { selections: new Map() } function onToggle(e, data) { this.setState(prev => ({ selections: SelectionUtils.toggle(prev.selections, data) })) }
- 不可变类方案
如果一定要保留类的封装性,就把Selections设计为不可变类,每次操作都返回全新的实例,禁止修改原有实例:
class Selections { constructor(map = new Map()) { this.map = map Object.freeze(this) // 冻结实例防止意外修改 } toggle(data) { const newMap = new Map(this.map) // 处理选择逻辑 return new Selections(newMap) } }
该方案下放在state中也不会有引用不变的问题,但依然存在不可序列化的缺陷,仅适合不需要状态持久化、调试回溯的场景。
行业使用情况
这种将带方法的自定义类实例放在state中的写法确实在行业中不常用,绝大多数React项目都会遵循官方的状态设计原则,优先将纯数据存在state中,逻辑抽离为外部纯函数或者自定义Hooks,仅会在state中存储DOM引用、定时器实例这类不需要参与渲染比较、不需要序列化的特殊对象。
内容的提问来源于stack exchange,提问作者Madhu
相关产品推荐
相关产品推荐

