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

时间:2016-03-01 21:07:00

标签: 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,
     //...
  },
  //...
}

这个课程(未经测试)

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;
  }
}

想象一下,我在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]
    },
  //...
}

这可以通过在构造函数

中注入dispatch来完成
//...
constructor({WorldCollection, dispatch})
//...

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

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

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

...
case "HEAL_PLAYER":
  return {
    ...state,
    life: state.life + action.hp
  };
...

临:

  • 恕我直言在我看来,只有一个地方只有一个所有州的地方。[/ li>

缺点:

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

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

{
  players:[
    001,
    002
    //...
  ]
}

这可以通过在每个dispatch中使用setter并在每个setter之后发送操作来完成

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

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

优点:

  • Redux方式通过不在reducers中放置逻辑而受到尊重
  • 不是"重复的状态" (如果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]

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

这是纯粹的redux / mobx比较中的优点/缺点对于我的情况

优点:

  • 少详细
  • 没有继承的模型(如在redux-orm中)
  • 已经评估了性能(所以我几乎不知道我将去哪里)
  • 不要写"意见"类模型中的reducer(只是mutators)

缺点:

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

3 个答案:

答案 0 :(得分:9)

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

您可能会发现this discussion有用,以及这两个示例:

答案 1 :(得分:6)

(MobX author). For a short answer on the questions about MobX:

Redo / undo can be implemented in two ways:

  1. Use the action / dispatcher model from flux: dispatch serializable actions, and interpret them to update the state model. This way you can build an append only action log and base undo / redo on that.
  2. Automatically serialize your state model into a state history (which uses structural sharing). The reactive-2015 demo demonstrates this nicely: https://github.com/mobxjs/mobx-reactive2015-demo/blob/master/src/stores/time.js. During this serialization you could also generate delta patches instead of a complete state tree if that is easier to process.

Single state tree:

  1. In MobX there should also be a single source of truth. The main difference with Redux is that it doesn't prescribe you were to store it. Nor does it enforce you to have a tree. A graph would do fine as well. Getting a snapshot of that graph can simple be done by leveraging mobx.toJson or by using solution previous point 2. of redo / undo.
  2. To make sure everything is in one connected graph (which you like), just create a root state objects that points to the player and world collection (for example). But unlike Redux, you don't have to normalize from there on. World can just have direct references to players and vice versa. A single state root object is created in the reactive-2015 demo as well: https://github.com/mobxjs/mobx-reactive2015-demo/blob/master/src/stores/domain-state.js

答案 2 :(得分:3)

您可能希望查看redux-orm,这几乎就是这样。它为Redux状态下的实际普通对象数据提供了一个类似于模型的外观,并且非常好地处理关系数据。