Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

813
Visualizações
Angular NGRX: Use withLatestFrom to call api only once. However, 'loader' is still set to true

I created the following code, trying to ensure the api is not called again when the state is set, and avoid using subscriptions in the component.

loadPackingList$ = createEffect(() => {
   return this.actions$.pipe(
     ofType(PackingPageActions.loadPackingList),
     switchMap((action) =>
         of(action).pipe(
            withLatestFrom(this.store.select(selectPackingList)),

            filter(([action, list]) => !list || list.length === 0),

            switchMap(([action, latest]) =>
               this.packingService.getPackingList(action.request).pipe(
               map((list) => PackingApiActions.loadPackingListSuccess({ list })),
               catchError((errors) =>
                   of(PackingApiActions.loadPackingListFail({ errors }))
             )
           )
         )
       )
     )
   );
});

This more or less works (the api is not called again) and is based amongst others on: How to send request to the server only once by using @NgRx/effects

However, the loading property is still set to true again, which should not happen:

export const packingReducer = createReducer(
  initialState,
  on(PackingPageActions.loadPackingList, (state) => ({
   ...state,
   loading: true,
})),

component's OnInit:

this.store.dispatch(
  PackingPageActions.loadPackingList({ request: this.PACKING_REQUEST })
);

Does anybody have an idea how to fix this?

Thanks!

over 4 years ago · Santiago Trujillo
1 Respostas
Responde à pergunta

0

first you can clean up the stream a bit:

loadPackingList$ = createEffect(() => {
   return this.actions$.pipe(
     ofType(PackingPageActions.loadPackingList),
     withLatestFrom(this.store.select(selectPackingList)),
     filter(([action, list]) => !list || list.length === 0),
     switchMap(([action, latest]) =>
       this.packingService.getPackingList(action.request).pipe(
         map((list) => PackingApiActions.loadPackingListSuccess({ list })),
         catchError((errors) =>
           of(PackingApiActions.loadPackingListFail({ errors }))
         )
       )
     )
   );
});

don't need the external switchMap into of, just redundant.

second, you'll need to also put a similar check into your reducer to make sure it doesn't get set unless the effect will actually go:

export const packingReducer = createReducer(
  initialState,
  on(PackingPageActions.loadPackingList, (state) => ({
   ...state,
   loading: !state.whatever.the.path.to.the.list.is?.length,
})),

or instead of filtering, you could just mock responses with the already fetched list:

loadPackingList$ = createEffect(() => {
   return this.actions$.pipe(
     ofType(PackingPageActions.loadPackingList),
     withLatestFrom(this.store.select(selectPackingList)),
     switchMap(([action, latest]) =>
       ((latest && latest.length) ? of(latest) : this.packingService.getPackingList(action.request)).pipe(
         map((list) => PackingApiActions.loadPackingListSuccess({ list })),
         catchError((errors) =>
           of(PackingApiActions.loadPackingListFail({ errors }))
         )
       )
     )
   );
});

you could do this in other ways, like having another action in there that signals the request is actually going out like loadPackingListRequest request that the effect will emit right before the request to signal it's actually happening, and this is what sets the loading state to true, probably cleaner this way since you don't have a hidden dependency on synchronizing code between your reducer and effects. but it's preference at this point.

EDIT:

the third action strategy would look a bit like this:

loadPackingList$ = createEffect(() => {
   return this.actions$.pipe(
     ofType(PackingPageActions.loadPackingList),
     withLatestFrom(this.store.select(selectPackingList)),
     filter(([action, list]) => !list || list.length === 0),
     switchMap(([action, latest]) => from([
       of(PackingApiActions.loadingPackingList()),
       this.packingService.getPackingList(action.request).pipe(
         map((list) => PackingApiActions.loadPackingListSuccess({ list })),
         catchError((errors) =>
           of(PackingApiActions.loadPackingListFail({ errors }))
         )
       )
     ]).pipe(mergeAll()))
   );
});

this way your effect will emit two actions, one indicating it is actually doing the loading, and another indicating if it failed or succeeded.

then you modify the reducer to look at the loading action instead to set loading to true:

export const packingReducer = createReducer(
  initialState,
  on(PackingApiActions.loadingPackingList, (state) => ({
   ...state,
   loading: true,
})),
over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda