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)