angular - ¿Cuál es la mejor manera de reaccionar a los cambios de estado de ngxs dentro de tu componente para eventos de carga o error?
Tengo un PostListComponent que itera sobre todas las publicaciones recuperadas del servidor y muestra un PostItemComponent individual. Estoy usando NGXS para la gestión del estado. Cuando inicio las solicitudes fetchPosts, cambio la propiedad del estado loading a true. Cuando la solicitud finaliza correctamente, la vuelvo a poner en false. Si una solicitud falla, actualizo el objeto de error en el estado y actualizo la propiedad message y la propiedad exception del objeto de error.
Esto es lo que tengo hasta ahora
`
export class PostListComponent implements OnInit {
private posts$: Observable<any> = this.store.select(state => state.posts);
private isLoading: boolean = false;
private error: object = {};
constructor(
public postsFacade: PostsFacadeService,
public moviesFacade: MoviesFacadeService,
private store: Store) {
}
ngOnInit() {
this.posts$.subscribe(
(state) => this.onLoadingEvent(state)
);
this.posts$.subscribe(
(error) => this.onErrorEvent(error)
)
// this.posts$.pipe(
// mergeMap(() => {
// // perform the subscription here and listen for state changes
// })
// )
}
onErrorEvent(error)
{
this.error = error.error;
}
onLoadingEvent(state)
{
this.isLoading = state.loading;
}
`
Estructura de mi tienda
`
@State<PostStateModel>({
name: 'posts',
defaults: {
posts: [],
loading: false,
error: {
message: "",
error: ""
},
}
})
`
Siento que hay una mejor manera de suscribirse a los cambios de estado dentro del almacén usando quizás el operador mergeMap de rxjs. No estoy seguro si eso es posible y si el enfoque actual es eficiente. ¿De qué otra manera puedo suscribirme y reaccionar a los cambios del almacén desde dentro de mi componente?
Respuesta #1
Así que algunas cosas:
- Necesitas definir algunas acciones de estado para tu clase de estado, como LoadPosts, por ejemplo.
- Deberías despachar esa acción (por ejemplo en tu ngOnInit), o en algún lugar antes de que el componente se cargue. La acción debería llamar a los servicios que realmente obtienen los datos, eliminando así esta dependencia de tu componente.
- Si el servicio usa Observables o Promesas, deberías manejar cuándo finalmente devuelven actualizando tu estado en ese momento.
Puedes crear tu observable mediante @Select. Fíjate en que de esta manera está tipado.
@Select(PostState) postState$: Observable;
Puedes usar el pipe async en tu componente y puede que no necesites suscribirte a postState$directamente en tu código (por ejemplo, para usar *ngFor para iterar a través de las publicaciones).
Debido a que tu estado incluye las publicaciones, la bandera de carga y el error, suscribirse al estado en su totalidad hará que tu función de suscripción o pipe asíncrono se ejecute sin importar cuál de estos cambie. Si creas métodos selectores con @Selector() en tu clase de estado, puedes suscribirte a estos para una granularidad más fina, por ejemplo, para reaccionar solo a la bandera de carga.
Respuesta #2
La respuesta parece ser bastante pesada e incluye configurar un interceptor para su aplicación. Luego, los errores emergerán hacia él.
ngxs.io/advanced/errors
Si no estás dispuesto a construir esa configuración de ciclo completo, puedes hacer una versión ligera informando un estado de respuesta "slice" resStatus.
export interface IReqStatus {
loading?: boolean;
error?: any;
success?: boolean;
action?: string; <-- optionally send along the action this belongs to
}
Ejemplo de acción realizando una solicitud a un punto de enlace de API
@Action(UpdateSomething)
updateSomething(ctx: StateContext<SomeStateModel>, { payload }: any): any {
// 1. update the resStatus to show "busy"
ctx.dispatch(new UpdateResStatus({ loading: true, action: 'updateSomething' }));
return this.apiService.patch('/something/endpoint', payload).pipe(
tap((res) => {
// 2. update resStatus to show success
ctx.dispatch(new UpdateResStatus(res));
}),
// 3. update the resStatus with errors
catchError((err: any) => {
return ctx.dispatch(new UpdateResStatus(err));
})
);
}
Usa una acción para gestionar el estado de la respuesta
@Action(UpdateResStatus)
updateResStatus(ctx: StateContext<SomeStateModel>, { payload }: any): Observable<any> {
let status = {
loading: false,
success: payload?.status == 'success',
error: payload?.status == 'success',
...(payload?.action && { action: payload?.action }),
};
ctx.setState(patch<SomeStateModel>({ reqStatus: status }));
return of(payload);
}
En mi caso, el servidor responde con una propiedad de estado de éxito. Pero si la solicitud está completamente rota, el catchError debería actualizar el reqStatus.
Puedes ver los estados de tus solicitudes con state.reqStatus en cualquier lugar. Y con suerte incluso puedes mantener tus async pipe intactos como:
<button [loading]="$any(reqStatus$ | async).loading" ...>
