问题
I'm beginning to use @ngrx and I'm facing an issue to which I can't seem to find an answer.
I illustrated it in this stackblitz : when a player picks a race, the specialty should be set back to undefined.
At first I thought I could use the reducer, but it doesn't have knowledge of the store without injection.
How should I approach this issue ?
(Note : this is a reproductible example, I know I can put a second dispatch in my functions. What I want is to do it automatically)
Related code :
import { Component } from '@angular/core';
import { Store, Action, ActionReducerMap } from '@ngrx/store';
@Component({
selector: 'my-app',
template:
`
<select #race (change)="dispatchRace(race.value)" placeholder="Select a race">
<option value="Elf">Elf</option>
<option value="Orc">Orc</option>
<option value="Dwarf">Dwarf</option>
</select>
<select #spec (change)="dispatchSpecialty(spec.value)" placeholder="Select a specialty">
<option value="Warrior">Warrior</option>
<option value="Berzerkrer">Berzerkrer</option>
<option value="Healer">Healer</option>
</select>
<p>
Current race: {{ (currentRace | async) || 'None' }}
</p>
<p>
Current Spec: {{ (currentSpecialty | async) || 'None' }}
</p>
`
})
export class AppComponent {
currentRace = this.store.select('race');
currentSpecialty = this.store.select('specialty');
constructor(public store: Store<CharacterState>) { }
dispatchRace(race) {
this.store.dispatch(new SetRaceAction(race));
}
dispatchSpecialty(spec) {
this.store.dispatch(new SetSpecialtyAction(spec));
}
}
const SET_RACE = '[RACE] Set';
const SET_SPECIALTY = '[CLASS] Set';
export class SetRaceAction implements Action {
readonly type = SET_RACE;
constructor(public race: string) { }
}
export class SetSpecialtyAction implements Action {
readonly type = SET_SPECIALTY;
constructor(public specialty: string) { }
}
export function raceReducer(state: string = undefined, action: SetRaceAction): string {
return action.race || state;
}
export function specialtyReducer(state: string = undefined, action: SetSpecialtyAction): string {
return action.specialty || state;
}
export interface CharacterState {
readonly race: string;
readonly specialty: string;
}
回答1:
If you add a console.log
in the raceReducer
you can see that all the actions are handled by all the reducers :
export function raceReducer(state: string = undefined, action: SetRaceAction): string {
console.log(action);
return action.race || state;
}
You just need to handle SetRaceAction
and SetSpecialtyAction
differently.
Edit of the stackblitz.
回答2:
So if I understand correctly: you want to dispatch one action that triggers another?
- this is best done using NGRX effects. Your effects file will listen for the action, and dispatch both when the action is caught. (excuse the snippet, i was having formatting issues)
// make sure to import rxjs 6 correctly
import { map, switchMap, tap } from 'rxjs/operators';
@Effect()
twoActionEvent = this.actions$
.ofType(appActions.FIRST_ACTION)
.pipe(
switchMap((action$: appActions.firstAction) => {
return //what ever you need in the first action
}),
map((firstActionPayload) => {
return {
type: appActions.FIRST_ACTION_SUCCESS,
payload: firstActionPayload
}
}),
tap(() => {
this.store.dispatch(new appActions.SecondAction(//payload))
})
);
constructor(private actions$: Actions,
private store: Store<AppState>) {}
- another way to do this would be to listen for the action in your app, by subscribing to a property in your store, and when that property is true: dispatch the second action
回答3:
I wanted to add one more solution, that I have done in my own implementations when I use flags in my store.
just change the flag in your state during the action you dispatch, for example:
import * as AppActions from './app.actions'; import * as handler from './app.models';
export function appReducer(
state: handler.AppState = handler.initialAppState,
action: AppActions.AppActions
) {
switch(action.type) {
case AppActions.SET_RACE_ACTION:
return {
...state,
race: action.payload
// set your flag in state
myFlag: undefined
}
}
}
来源:https://stackoverflow.com/questions/52498684/ngrx-dispatch-one-event-from-a-store-to-another