简体   繁体   中英

NgRx : dispatch one event from a store to another

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

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.

So if I understand correctly: you want to dispatch one action that triggers another?

  1. 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>) {} 

  1. 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

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

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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