Creé el siguiente código, tratando de asegurarme de que no se vuelva a llamar a la API cuando se establece el estado, y de evitar el uso de suscripciones en el componente.
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 })) ) ) ) ) ) ); });
Esto funciona más o menos (la API no se vuelve a llamar) y se basa, entre otros, en: Cómo enviar una solicitud al servidor solo una vez usando @NgRx/effects
Sin embargo, la propiedad de carga aún se establece en verdadero nuevamente, lo que no debería suceder:
export const packingReducer = createReducer( initialState, on(PackingPageActions.loadPackingList, (state) => ({ ...state, loading: true, })),
OnInit del componente:
this.store.dispatch( PackingPageActions.loadPackingList({ request: this.PACKING_REQUEST }) );
¿Alguien tiene una idea de cómo solucionar esto?
¡Gracias!
primero puedes limpiar un poco la transmisión:
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 })) ) ) ) ); });
no necesita el switchMap
externo en of
, simplemente redundante.
en segundo lugar, también deberá realizar una verificación similar en su reductor para asegurarse de que no se establezca a menos que el efecto realmente se vaya:
export const packingReducer = createReducer( initialState, on(PackingPageActions.loadPackingList, (state) => ({ ...state, loading: !state.whatever.the.path.to.the.list.is?.length, })),
o en lugar de filtrar, simplemente puede simular respuestas con la lista ya obtenida:
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 })) ) ) ) ); });
podría hacer esto de otras maneras, como tener otra acción allí que indique que la solicitud realmente se está ejecutando, como la solicitud loadPackingListRequest
que el efecto se emitirá justo antes de la solicitud para indicar que realmente está sucediendo, y esto es lo que establece el estado de loading
en verdadero , probablemente más limpio de esta manera ya que no tiene una dependencia oculta en la sincronización del código entre su reductor y efectos. pero es preferencia en este punto.
EDITAR:
la tercera estrategia de acción se vería un poco así:
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())) ); });
de esta manera, su efecto emitirá dos acciones, una que indica que realmente está cargando y otra que indica si falló o tuvo éxito.
luego modifica el reductor para mirar la acción de carga en lugar de establecer la carga en verdadero:
export const packingReducer = createReducer( initialState, on(PackingApiActions.loadingPackingList, (state) => ({ ...state, loading: true, })),