You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React状态中存储自定义类实例是否合理?跨页表格选择实现疑问

你的实现存在的潜在问题

你当前的实现虽然能正常运行,但确实不符合React的状态设计最佳实践,存在几个容易被忽略的风险:

  • 渲染更新问题:React的状态更新默认做浅比较,如果你的Selections类的toggle方法是修改原有实例后返回this,那setState传入的对象和原state引用完全相同,React会认为状态没有变化,后续如果用到React.memo、PureComponent或者浅比较的性能优化逻辑,会出现状态修改后组件不重新渲染的bug,你现在没遇到问题大概率是其他状态更新带动了重渲染。
  • 不符合可序列化要求:React官方明确推荐state中仅存储可序列化为JSON的纯数据,类实例包含方法、可能存在循环引用,后续如果要做状态持久化、服务端渲染、时间旅行调试,都会遇到无法序列化的问题,也没法直接在React DevTools中直观查看所有状态值。
  • 可变状态风险:类实例的内部属性可以被任意方法修改,会打破React状态的可预测性,出现问题时很难追溯状态变更的链路。

复用与可测试性的替代方案

你提到的跨组件复用、单独单元测试的收益,完全可以用更符合React生态的方式实现,不需要把类实例存在state里:

  1. 纯函数工具集方案(更推荐)
    把选择逻辑抽成独立的纯函数,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)
  }))
}
  1. 不可变类方案
    如果一定要保留类的封装性,就把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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 05:24:04