I am working on a relative simple app where i want to learn more about ngrx, redux and angular 2.
I'll explain briefly about the setup of my app, my appstate and the reducers.
In my app i want to draw certain objects (meshes) on my screen using a webgl framework. I want to add a mesh through the ngrx store by creating simple objects with the properties of those meshes and save those in the app state. Whenever a 'mesh' has been added i want to use ngrx sideeffects to draw the mesh on screen using a service which has access to the webgl framework.
My (simplified) appstate looks like this:
export interface AppState {
meshList: MeshListReducer.MeshListState
}
To add a mesh i have created the following reducer functions:
case MeshActions.ADD_MESH: {
return Object.assign({
entities: [meshAction.payload, ...state.entities]
});
}
case MeshActions.ADD_MESH_SUCCESS:
case MeshActions.UPDATE_MESH: {
// This reducer function is only being called once, see below for further explanation.
return Object.assign({}, state, {
entities: state.entities.map(meshItem => {
// If we can find a mesh with this id, use the sub reducer to update it.
if (meshItem.uuid === meshAction.payload.uuid)
return meshSubReducer(meshItem, meshAction);
return meshItem;
})
});
}
default: {
return state;
}
}
export function meshSubReducer(mesh: BaseMesh, meshAction: MeshAction): BaseMesh {
switch (meshAction.type) {
case MeshActions.ADD_MESH_SUCCESS:
case MeshActions.UPDATE_MESH:
return Object.assign({}, meshAction.payload);
default:
return mesh;
}
}
And finally my effects class which contains the call to the webgl framework service, and calling the succes action:
@Effect()
addMesh$ = this.actions$
.ofType(MeshActions.ADD_MESH)
.map((action: MeshAction) => action.payload)
.switchMap((payload: BaseMesh) => this._worldService.addMeshToWorld(payload))
.map(payload => this._meshActions.addMeshSuccess(payload));
And the worldService.addMeshToWorld function:
public addMeshToWorld(mesh: BaseMesh): Promise<BaseMesh> {
let p = new Promise<BaseMesh>((resolve, reject) => {
let renderer = this._meshRendererFactory.createMeshRenderer(mesh);
// Every call to the ngrx effects always enters this function, and the component is sucessfully added to the canvas.
renderer.addTo(this._world.app).then((component: WHS.Component) => {
resolve(Object.assign({}, mesh, {
rendererId: (<any>component).native.id
}));
});
});
return p;
}
And i am invoking all of these functions using these dispatch methods:
let soccerBall = new SphereMesh({
geometry: {
heightSegments: 20,
widthSegments: 20,
radius: 5
},
gameMeshType: Enums.GameMeshType.Sphere,
uuid: this._meshHelper.generateUUID(),
meshMaterial: {
color: 0xF2F2F2
}
});
let soccerBall2 = new SphereMesh({
geometry: {
heightSegments: 20,
widthSegments: 20,
radius: 5
},
gameMeshType: Enums.GameMeshType.Sphere,
uuid: this._meshHelper.generateUUID(),
meshMaterial: {
color: 0xF2F2F2
}
});
// Dispatching these straight after each other will cause the succes action to only be dispatched once.
this._store.dispatch(this._meshActions.addMesh(soccerBall));
this._store.dispatch(this._meshActions.addMesh(soccerBall2));
The problem i am having is that after the "ADD_MESH" side effect is being called and after adding the mesh to the screen, the effect is only calling the final 'map' action once, so the ADD_MESH_SUCCES reducer function is only called for one mesh, and not the other, see screenshot:
The weird thing is that when i add the second dispatch in a timeout, the success call is now suddenly being called correctly for all dispatched items:
this._store.dispatch(this._meshActions.addMesh(soccerBall));
setTimeout(() => {
// Now succes action is suddenly being called twice like it should..
this._store.dispatch(this._meshActions.addMesh(soccerBall2));
}, 1500);
What could cause this weird behavior? I thought my app was fairly simple but it seems i am missing something.
I am using the following package versions (i picked out the packages that i thought would be most useful to mention here:
"@angular/core": "^5.0.0",
"rxjs": "^5.5.2",
"@ngrx/effects": "^5.2.0",
"@ngrx/store": "^5.0.0",
"typescript": "~2.4.2"