Search code examples
angularngrxngrx-storengrx-effects

In NgRx, where to transform the response data from a server (got through side effects) to a state which the component understands?


I am new to NgRx hence the confusion. I have a simple component which shows a list of objects e.g. Orders. It gets the list of Orders to be shown from backend using an Effect. Only problem is I want to transform this response from the BE into a simple list of objects to show in the component. I am confused as to where to transform it. I see 4 options:

  1. Transform it in the service that gets the list from the backend - Quite simple to implement not sure if its the best way.
  2. Transform it in the effect - If here then why not in the service?
  3. Transform it using the reducer - Seems like a valid place.
  4. Transform it using a selector - We won't need a view state as the view state will be derived from the selector every time a component is opened. May be even make it slow because it has to transform the state every time.

This is my understanding so far. I would like to know if its correct and I would also like to know the best practice to handle this case which seems should be fairly common in applications.


Solution

  • you have:

    • a component where you need your list
    • a service where you do http requests
    • a state, ngrx

    steps:

    1. a) from the component, dispatch an action - to get the list b) in component, listen to the listAction (to receive new data)

       ngOnInit() {
           this.store.dispatch(getListAction); // this will fire the action
      
       this.store.pipe(
          select(fromStore.selectList)
       ).subscribe(yourList => {
          // here you have the list
       });
      

      }

    1. actions.ts

       export enum Types {
             GET_LIST = '[Store Type] Get List',
             GET_LIST_SUCCESS = '[Store Type] Get List Success',
             GET_LIST_FAILED = '[Store Type] Get List Failed',
           } 
      
       export const getListAction = createAction(
         Types.GET_LIST // this will be fired from component, and will fire the store effect
       );
       export const getListSuccessAction = createAction(
         Types.GET_LIST_SUCCESS, // this will be fired when service return the list
         props<{list: obj_type[]}>
       );
      export const getListFailedAction = createAction(
         Types.GET_LIST_FAILED, // this will be fired when service throws error
         props<{msg: string}>
       );
      
    2. effects.ts

        @Injectable()
        export class MyEffects {
        
         constructor(service: YourService) {
        }
            
             getList$ = createEffect(() =>
                this.actions$.pipe(
                  ofType(getListAction),
                  switchMap(() =>
                    this.your_service.getList().pipe(
                      tap((list) => getListSuccessAction({list})),
                      catchError((error) =>
                        of(getListFailedAction({ msg: error.message }))
                      )
                    )
                  )
                )
              );
            }
    
    1. reducer.ts

      export const reducer = createReducer(
        initialState,
        on(getListAction, (state) => ({
          ...state,
          request: PENDING
        })),
        on(getListSuccessAction, (state, { list}) => ({
          // your component store.pipe(select) will be fired with data from here
          ...state,
          list: 
          request: COMPLETED
        })),
        on(getListFailedAction, (state, { msg }) => ({
          ...state,
          error: {msg},
          request: FAILED
        }))
      );````
      
      
    2. selectors.ts

        export const selectState = (state: AppState) => state;
        
        export const selectList =
          createSelector(selectState , (state) =>
            state.list);