简体   繁体   English

如何在Redux中使用类模型(带有Mobx选项)

[英]How to use class model with Redux (with a Mobx option)

EDIT: I finally choosed Mobx.js, refer to @mweststrate answer for details. 编辑:我终于选择了Mobx.js,有关详细信息,请参阅@mweststrate答案。

All learning ressources about redux show how to use it with plain object models. 所有有关redux的学习资源都显示了如何将其与普通对象模型一起使用。 But I can't figure out how to use it when you use some es6 Class models. 但是当您使用某些es6类模型时,我不知道如何使用它。

For example, let's take this state shape: 例如,让我们采用以下状态形状:

{
 players:{
   000:{
     life:56,
     lvl:4,
     //...
  },
   023:{
     life:5,
     lvl:49,
     //...
  },
   033:{
     life:679,
     lvl:38,
     //...
  },
   067:{
     life:560,
     lvl:22,
     //...
  },
  //...
}

And this class (not tested) 和该类(未经测试)

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

Imagine I have a collection of 100 players in WorldCollection. 想象一下,我在WorldCollection中有100位玩家。 What is the best way? 什么是最好的方法?

Take 1: copying all properties from instance to the state tree 采取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]
    },
  //...
}

This could be done by injecting dispatch in the constructor 这可以通过在构造函数中注入dispatch来完成

//...
constructor({WorldCollection, dispatch})
//...

Dispatch an action in each setter. 在每个设置器中调度一个动作。

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

And put all the logic in reducers (setter logic being deterministic and atomic). 并将所有逻辑放入简化器(确定器逻辑是确定性的和原子性的)。

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

Pro: 优点:

  • IMHO It seems to me more redux way to have only one place where all the state is. 恕我直言,在我看来,只有一种状态是所有状态的一种替代方法。

Cons: 缺点:

  • All logic is decentralized from the model in another place. 所有逻辑都从模型分散在另一个地方。 I don't like to multiply files. 我不喜欢复制文件。 But maybe it is not a real problem? 但是也许这不是一个真正的问题?
  • Redux says the logic has to be put in actions, not in reducers. Redux表示必须将逻辑放在行动中,而不是归约器。
  • The state takes twice more memory. 状态占用的内存增加了两倍。 I saw that immutables.js could optimize this but I am not sure. 我看到immutables.js可以优化它,但是我不确定。

Take 2: Storing only ids in the redux state tree 习题2:仅将ID存储在Redux状态树中

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

This could be done by also using dispatch in each setter and dispatch an action after each setter 这也可以通过在每个setter使用dispatch在每个setter之后调度动作来完成

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

When the new tree state is changed. 当新的树状状态改变时。 I call mapStateToProps and WorldCollection.getPlayer() to retrieve the right instance and map its properties to the view. 我调用mapStateToPropsWorldCollection.getPlayer()来检索正确的实例并将其属性映射到视图。

Pros: 优点:

  • Redux way is respected by not putting logic in reducers 不将逻辑放在化简器中来尊重Redux方式
  • Not "duplicated state" (if Immutables.js can't optimise this) 不是“重复状态”(如果Immutables.js无法对此进行优化)
  • Logic is in the model (makes more sense for me) 逻辑在模型中(对我来说更有意义)

Cons: 缺点:

  • Redux state does not represent the whole state Redux状态并不代表整个状态

I hope I have not simplified the case too much. 我希望我没有过多简化此案。 My point is to clarify if/how redux could be use with some class models. 我的观点是阐明是否/如何在某些类模型中使用redux。

Take 3: Use Mobx.js instead/with Redux 范例3:改用Mobx.js /与Redux搭配使用

--- very pre-experimental here --- ---这里是非常实验性的---

I discovered Mobx.js a week ago and its simplicity/perf had me. 一周前,我发现了Mobx.js,它的简单/完美吸引了我。

I think we could observe each class members (which together form the app state) 我认为我们可以观察每个班级成员(它们共同构成应用程序状态)

  @observable life; //int
  @observable lvl; //int
  @observable buffs; //[objects]
  @observable debuffs; //[objects]
  @observable inventory; //[objects]

and somewhere else have a class which builds the state tree, maybe Redux could make sense here? 在其他地方有一个建立状态树的类,也许Redux在这里有意义? (Note I have no clue how to do this part. Have to dig more deeply in Mobx) (请注意,我不知道如何执行此部分。必须更深入地研究Mobx)

This is pros/cons in a pure redux/mobx comparaison for my case . 就我的情况而言,这在纯redux / mobx比较中是利弊。

Pros: 优点:

  • Less verbose 不太冗长
  • No Model to inherited from (like in redux-orm) 没有要继承的模型(例如在redux-orm中)
  • Performance has been evaluated (So I barely know where I would be going to) 性能已经过评估(因此我几乎不知道要去哪里)
  • Don't write "opiniated" reducers in the class model (just mutators) 不要在类模型中编写“优化的” reducer(仅是变种器)

Cons: 缺点:

  • No idea how to implement a redo/undo (or a jitter buffer in game dev) 不知道如何实现重做/撤消(或游戏开发中的抖动缓冲区)
  • It does not seem to be a "tree" like in redux to have the whole state in a blink (for me it is the killer feature of redux) 看起来好像不是像redux那样的“树”,让整个状态一瞬间消失(对我来说,这是redux的杀手级功能)

I would like to add that if you were to go with Redux you would not store state in classes at all . 我想补充一点,如果要使用Redux ,则根本不会将状态存储在类中 In Redux, this logic would be described in reducers which would operate on plain objects rather than class instances. 在Redux中,将在化简器中描述此逻辑,该化简器将对普通对象而不是类实例进行操作。 You would keep the data normalized so each entity is kept in an object map by its ID, and any reference to child entities would be an array of IDs rather than a real reference. 您将数据标准化,以便每个实体都通过其ID保留在对象图中,并且对子实体的任何引用将是ID数组,而不是真实的引用。 You would then write selectors to reconstruct the parts of the data you care about for rendering. 然后,您将编写选择器以重构您关心的渲染数据部分。

You might find this discussion helpful, as well as these two examples: 您可能会发现此讨论以及下面的两个示例很有帮助:

(MobX author). (MobX作者)。 For a short answer on the questions about MobX: 对于有关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. 使用flux中的操作/调度程序模型:调度可序列化的操作,并解释它们以更新状态模型。 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 . react-2015演示很好地演示了这一点: 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. 在MobX中,也应该有一个单一的事实来源。 The main difference with Redux is that it doesn't prescribe you were to store it. Redux的主要区别在于它并不要求您存储它。 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. 通过利用mobx.toJson或使用重做/撤销的第2点解决方案,可以简单地获取该图的快照。
  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. 但是与Redux不同,您无需从那里进行标准化。 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 在react-2015演示中也创建了一个状态根对象: https : //github.com/mobxjs/mobx-reactive2015-demo/blob/master/src/stores/domain-state.js

You might want to look at redux-orm , which pretty much does this already. 您可能想看看redux-orm ,它已经差不多完成了。 It provides a nice Model-like facade over the actual plain object data in your Redux state, and handles relational data very nicely. 它为Redux状态下的实际普通对象数据提供了一个类似于Model的外观,并且可以很好地处理关系数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM