ITKeyword,专注技术干货聚合推荐

注册 | 登录

解决javascript - How to use class model with Redux (with an Mobx option)

itPublisher 分享于

2020腾讯云10周年活动,优惠非常大!(领取2860元代金券),
地址https://cloud.tencent.com/act/cps/redirect?redirect=1040

2020阿里云最低价产品入口,含代金券(新老用户有优惠),
地址https://www.aliyun.com/minisite/goods

EDIT: I finally choosed Mobx.js, refer to @mweststrate answer for details.

All learning ressources about redux show how to use it with plain object models. But I can't figure out how to use it when you use some es6 Class models.

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. What is the best way?

Take 1: copying all properties from instance to the state tree

{
  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

//...
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.
  • The state takes twice more memory. I saw that immutables.js could optimize this but I am not sure.

Take 2: Storing only ids in the redux state tree

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

This could be done by also using dispatch in each setter and dispatch an action after each 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.

Pros:

  • Redux way is respected by not putting logic in reducers
  • Not "duplicated state" (if Immutables.js can't optimise this)
  • Logic is in the model (makes more sense for me)

Cons:

  • Redux state does not represent the whole state

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.

Take 3: Use Mobx.js instead/with Redux

--- very pre-experimental here ---

I discovered Mobx.js a week ago and its simplicity/perf had me.

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? (Note I have no clue how to do this part. Have to dig more deeply in Mobx)

This is pros/cons in a pure redux/mobx comparaison for my case.

Pros:

  • Less verbose
  • No Model to inherited from (like in 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)

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)
javascript state redux mobx
|
  this question
edited Mar 3 '16 at 18:08 asked Mar 1 '16 at 21:07 DanielN 717 6 29

 | 

3 Answers
3

解决方法

You might want to look at redux-orm, which pretty much does this already. It provides a nice Model-like facade over the actual plain object data in your Redux state, and handles relational data very nicely.


|
  this answer
answered Mar 1 '16 at 22:29 markerikson 6,490 1 17 35      Soons very good. However I wonder what are the performances (since the author "warns" about it) with a lot of entities. (My client could handle 100+ at the same time, but the server - whit a lot of RAM - have to manage millions of them). I am currently looking at Mobx.js package to compare. But I don't know if I could use Redux with Mobx.js. Seem too different. –  DanielN Mar 2 '16 at 9:01      I will edit to have a (non proved) third take with mobx.js. –  DanielN Mar 2 '16 at 9:06

 | 

I would like to add that if you were to go with Redux you would not store state in classes at all. In Redux, this logic would be described in reducers which would operate on plain objects rather than class instances. 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. 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:


|
  this answer
edited Mar 5 '16 at 12:40 answered Mar 5 '16 at 11:54 Dan Abramov 94.5k 35 222 355

 | 

(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

|
  this answer
answered Mar 2 '16 at 10:46 mweststrate 2,206 4 12      Very cool! The graph possibility could perfectly fit with my needs (tree was a kind of fallback in my architecture). I have now to assimilate all this infos first before take any conclusion. Thanks you very much! –  DanielN Mar 2 '16 at 10:54      What do you mean by leveraging mobx.toJson? Is it a package or I missed something. –  DanielN Mar 2 '16 at 13:44      Its like JSON.stringify, but supports cycles. Creates non-observable clones of your object graph: mobxjs.github.io/mobx/refguide/tojson.html –  mweststrate Mar 2 '16 at 14:19      Ok thx. Do you have an idea of the memory impact to maintain a state graph aside all models instance? In short, are they just references memory adresse pointers, very light, or sync copies doubling the memory size? –  DanielN Mar 2 '16 at 15:45 1   Do you mind if I check @makerikson answer? Indeed redux-orm perfectly fits the need but after comparing both architectures I have gone with Mobx. So you lose the answer but win a user. Sounds fair :) ? –  DanielN Mar 2 '16 at 20:15  |  show more comments


相关阅读排行


相关内容推荐

最新文章

×

×

请激活账号

为了能正常使用评论、编辑功能及以后陆续为用户提供的其他产品,请激活账号。

您的注册邮箱: 修改

重新发送激活邮件 进入我的邮箱

如果您没有收到激活邮件,请注意检查垃圾箱。