如何使用Redux的类模型(使用Mobx选项)

dag*_*oin 8 javascript state redux mobx

编辑:我最终选择了Mobx.js,详情请参阅@mweststrate答案.

关于redux的所有学习资源都展示了如何将它与普通对象模型一起使用.但是当你使用一些es6 Class模型时,我无法弄清楚如何使用它.

例如,让我们采用这种状态形状:

{
 players:{
   000:{
     life:56,
     lvl:4,
     //...
  },
   023:{
     life:5,
     lvl:49,
     //...
  },
   033:{
     life:679,
     lvl:38,
     //...
  },
   067:{
     life:560,
     lvl:22,
     //...
  },
  //...
}
Run Code Online (Sandbox Code Playgroud)

而这堂课(未经测试)

class Player{
  id; //int
  life; //int
  lvl; //int
  buffs; //[objects]
  debuffs; //[objects]
  inventory; //[objects]

  _worldCollection; //this class know about the world they belongs to.

  constructor({WorldCollection}){
    this._worldCollection = WorldCollection;
  }

  healPlayer(targetId, hp){
   this._worldCollection.getPlayer(targetId).setHealth(hp);
  }

  // setter
  setHealth(hp){
    this.life += hp;
  }
}
Run Code Online (Sandbox Code Playgroud)

想象一下,我在WorldCollection中收集了100名玩家.什么是最好的方法?

取1:将所有属性从实例复制到状态树

{
  players:{
    001:{
      life: 45,
      lvl: 4,
      buffs: [objects]
      debuffs:[objects]
      inventory:[objects]
    },
    034:{
      life: 324,
      lvl: 22,
      buffs: [objects]
      debuffs:[objects]
      inventory:[objects]
    },
    065:{
      life: 455,
      lvl: 45,
      buffs: [objects]
      debuffs:[objects]
      inventory:[objects]
    },
  //...
}
Run Code Online (Sandbox Code Playgroud)

这可以通过dispatch在构造函数中注入来完成

//...
constructor({WorldCollection, dispatch})
//...
Run Code Online (Sandbox Code Playgroud)

在每个setter中发送一个动作.

// setter
setHealth(hp){
  this.life += hp;
  dispatch({type:"HEAL_PLAYER", data:{id:this.id})
}
Run Code Online (Sandbox Code Playgroud)

并将所有逻辑放在reducer中(setter逻辑是确定性的和原子的).

...
case "HEAL_PLAYER":
  return {
    ...state,
    life: state.life + action.hp
  };
...
Run Code Online (Sandbox Code Playgroud)

优点:

  • 恕我直言在我看来更多的还原方式只有一个地方所有的州.

缺点:

  • 所有逻辑都在另一个地方从模型中分散出来.我不喜欢繁殖文件.但也许这不是一个真正的问题?
  • Redux说逻辑必须付诸行动,而不是减速器.
  • 状态需要两倍的内存.我看到immutables.js可以优化这个,但我不确定.

取2:仅在redux状态树中存储id

{
  players:[
    001,
    002
    //...
  ]
}
Run Code Online (Sandbox Code Playgroud)

这可以通过使用也完成dispatch每个setter和每个二传手之后分派行动

// setter
setHealth(hp){
  this.life += hp;
  dispatch({type:"PLAYER_UPDATED", data:{id:this.id})
}
Run Code Online (Sandbox Code Playgroud)

当新树状态改变时.我调用mapStateToPropsWorldCollection.getPlayer()检索正确的实例并将其属性映射到视图.

优点:

  • 通过不将逻辑放在reducer中来尊重Redux方式
  • 不是"重复状态"(如果Immutables.js无法优化此项)
  • 逻辑在模型中(对我来说更有意义)

缺点:

  • Redux州并不代表整个州

我希望我没有过多地简化这个案子.我的观点是澄清redux是否可以与某些类模型一起使用.

3:使用Mobx.js代替/使用Redux

---这里非常实验---

我一周前发现了Mobx.js,它的简单/性能让我感到高兴.

我想我们可以观察每个班级成员(它们共同形成应用程序状态)

  @observable life; //int
  @observable lvl; //int
  @observable buffs; //[objects]
  @observable debuffs; //[objects]
  @observable inventory; //[objects]
Run Code Online (Sandbox Code Playgroud)

而在其他地方有一个构建状态树的类,也许Redux在这里有意义吗?(注意我不知道如何做这个部分.必须在Mobx中深入挖掘)

对于我的案例,这是纯粹的redux/mobx比较中的优缺点.

优点:

  • 不那么冗长
  • 没有继承的模型(如在redux-orm中)
  • 性能已被评估(所以我几乎不知道我会去哪里)
  • 不要在类模型中写出"意见"的reducer(只是mutators)

缺点:

  • 不知道如何在游戏开发中实现重做/撤消(或抖动缓冲)
  • 它似乎不是像redux中的"树"一样让整个状态眨眼(对我而言,它是redux的杀手特征)

Dan*_*mov 10

我想补充一点,如果你要使用Redux,你根本不会将状态存储在类中.在Redux中,这个逻辑将在reducers中描述,reducers将在普通对象而不是类实例上运行.您将保持数据规范化,以便每个实体通过其ID保存在对象映射中,并且对子实体的任何引用都是ID数组而不是实际引用.然后,您将编写选择器来重建您关注的数据部分以进行渲染.

您可能会发现此讨论很有用,以及这两个示例:


mwe*_*ate 6

(MobX作者).有关MobX的问题的简短回答:

重做/撤消可以通过两种方式实现:

  1. 使用flux中的action/dispatcher模型:dispatch serializable actions,并解释它们以更新状态模型.这样,您可以构建仅附加操作日志和基本撤消/重做.
  2. 自动将状态模型序列化为状态历史(使用结构共享).reactive-2015演示很好地证明了这一点:https://github.com/mobxjs/mobx-reactive2015-demo/blob/master/src/stores/time.js.在此序列化过程中,如果更容易处理,您还可以生成增量补丁而不是完整的状态树.

单状态树:

  1. 在MobX中,也应该有一个单一的事实来源.与Redux的主要区别在于它没有规定你要存储它.它也没有强制你拥有一棵树.图表也可以.通过利用mobx.toJson或使用解决方案之前的重做/撤消点2,可以轻松获得该图的快照.
  2. 要确保所有内容都在一个连接的图形中(您喜欢),只需创建一个指向播放器和世界集合的根状态对象(例如).但与Redux不同,您不必从那里开始标准化.世界可以直接引用玩家,反之亦然.在reactive-2015演示中也创建了一个状态根对象:https://github.com/mobxjs/mobx-reactive2015-demo/blob/master/src/stores/domain-state.js


mar*_*son 4

您可能想看看redux-orm,它几乎已经做到了这一点。它在 Redux 状态中的实际普通对象数据上提供了一个很好的类似模型的外观,并且非常好地处理关系数据。