0

I am new to angular + ngrx and have a question about proper control flow at scale. Suppose I have a Cars component that can can load a list of cars and can create a new car. Once those effects execute, they'll dispatch SUCCESS and FAILURE actions. In my ngOnInit, I am subscribing to each of those success/failure actions to perform appropriate - something like:

ngOnInit() {
    this.actions$
      .pipe(
        ofType<fromCarsActions.GetCarsSuccessAction>(fromCarsActions.CarsActionTypes.GET_CARS_SUCCESS),
        takeUntil(this.unsubscribe$)
      )
      .subscribe(results => {
        // do something if load all cars SUCCEEDS
      });


      this.actions$
      .pipe(
        ofType<fromCarsActions.GetCarsFailureAction>(fromCarsActions.CarsActionTypes.GET_CARS_FAILURE),
        takeUntil(this.unsubscribe$)
      )
      .subscribe(results => {
        // do something if load all cars FAILS
      });


      this.actions$
      .pipe(
        ofType<fromCarsActions.AddCarSuccessAction>(fromCarsActions.CarsActionTypes.ADD_CAR_SUCCESS),
        takeUntil(this.unsubscribe$)
      )
      .subscribe(results => {
        // do something if add car SUCCEEDS
      });


      this.actions$
      .pipe(
        ofType<fromCarsActions.AddCarsFailureAction>(fromCarsActions.CarsActionTypes.ADD_CAR_FAILURE),
        takeUntil(this.unsubscribe$)
      )
      .subscribe(results => {
        // do something if add car FAILS
      });
}

Subscribing to all these SUCCESS/FAILURE actions is fine if there's only a few, but can easily get out of hand very quickly for more complex components.

Another option I could do is dispatch the appropriate action in the effect itself, but that would not make the effect very reusable.

So my question is what's the best practice for a component that uses NGRX to communicate with the backend to easily manage subscribing to / listening to multiple different effect success/failures?

I understand this is a bit of an abstract question so please let me know if I can clarify anything.

John Grayson
  • 1,378
  • 3
  • 17
  • 30

2 Answers2

2

You do not need to subscribe to any actions.

You should to react to actions in reducer and in effects. And effect should eventually dispatch another action in the end. But you can use it for side effects, say navigation.

So most of the things that you have in mind in your ngOnInit should be in the reducer.

kvetis
  • 6,682
  • 1
  • 28
  • 48
  • Right, but from what i understand, a reducer should just change the state. So for example, if I need to navigate to a page after successfully adding a car - where should that be handled? Or are you saying that after successfully adding a car, my effect needs to dispatch an action that navigates to another page? Thank you for responding! – John Grayson Feb 23 '21 at 16:05
  • After successfully adding a car you can subscribe to the selector. create a selector for newCarAdded when you update the store when the car is added the selector will get subscribed. you can access this selector any where in your app that is the advantage. – Guruprasad mishra Feb 23 '21 at 17:44
  • I disagree. Any side effects should go into the effects section. So navigation should go there. [See this answer](https://stackoverflow.com/a/50589384/1956072) – kvetis Feb 25 '21 at 08:20
0

you trigger the Action

Action will trigger a Reducer( will update the store only) or Effect(handles the service- responsible to fetch data from DB)

then you can subscribe to the Selector to get the change.

In your case create Action -

export const loadCars = createAction('Load Car data');

export const getcarsSuccess = createAction('Get Car data Success', props<{ carData: any }>());

export const getCarsError = createAction('Get Car data Error', props<{ errorResponse: any}>());

export const createCars = createAction('Add Car data');

export const createCarsSuccess = createAction('Add Car data Success', props<{ carData: any }>());

export const createCarsError = createAction('Add Car data Error', props<{ errorResponse: any}>());

the above action can trigger a effect or reducer

In Effect:

 @Effect()
  public loadCarData$ = createEffect(() => this.actions$.pipe(
    ofType(actions.loadCars ) ...

In Reducer:

 const initReducer = createReducer(
  initialIState,
  on(actiona.loadCarData, state => (state)))

In selector:

export const carDataLoaded = (state: fromReducer.carData) => state.carData ;

export const getCarData = createSelector(
  getInitState,
  carDataLoaded 
);

Finally In the component:

You can subscribe to selector getCarData, so component - action - effect- reducer - selector - component

In Comp

 private carStore: Store<carStore> 
  this.carStore.dispatch(new loadCars ()); //dispatch load Cars Action
 this.carStore.pipe(select(
            getCarData })
        ).subscribe(cars => console.log(cars)) // subscribe to selector

Note: You can subscribe to any action directly from component as you have mentioned but that will not serve the purpose of using NGRX State Management.

Update NGRX Store From anywhere in Angular App and Access the store data from anywhere you want in the app. Without worrying about maintaining the state of data.

  • thanks! so basically, the trick is to have the reducers kick off actions to update the state - and in the component, subscribe to the state to execute further actions – John Grayson Feb 23 '21 at 19:47
  • No, that is not correct. Do not listen on state changes in your component to trigger further actions. Further actions should always be triggered in effects. – kvetis Feb 25 '21 at 08:22