Каков наилучший способ реагировать на изменения состояния ngxs в вашем компоненте для событий загрузки или ошибок?
У меня есть PostListComponent, который перебирает все сообщения, полученные с сервера и отображает отдельные PostItemComponent. Я использую NGXS для управления состоянием. Когда я инициирую запросы fetchPosts, я изменяю свойство загрузки состояния: true. Когда запрос завершается успешно, я изменяю его обратно на false. Если запрос не выполняется, я обновляю объект ошибки в состоянии и обновляю свойство сообщения объекта ошибки и свойство исключения.
Вот что у меня так далеко
`
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;
}
`
Структура моего магазина
`
@State<PostStateModel>({
name: 'posts',
defaults: {
posts: [],
loading: false,
error: {
message: "",
error: ""
},
}
})
`
Я чувствую, что есть лучший способ подписаться на изменения состояния в магазине с использованием, возможно, оператора rxjs mergeMap. Я не уверен, возможно ли это и эффективен ли нынешний подход. Как еще я могу подписаться и реагировать на изменения магазина внутри моего компонента?
2 ответа
Итак, несколько вещей:
- Вам нужно определить некоторые действия для вашего класса состояний - например, LoadPosts.
- Вы должны отправить это действие (скажем, в вашем ngOnInit) или куда-то до загрузки компонента. Действие должно вызывать сервисы, которые фактически извлекают данные, тем самым удаляя эту зависимость из вашего компонента.
- Если служба использует Observables или Promises, вы должны обработать, когда они наконец вернутся, обновив ваше состояние в этот момент.
Вы можете создать свою наблюдаемую с помощью @Select. Обратите внимание, что таким образом это напечатано.
@Select (PostState) postState $: Observable;
Вы можете использовать асинхронный канал в своем компоненте, и вам может не потребоваться подписка на postState $ непосредственно в вашем коде (например, для использования * ngFor для итерации по сообщениям).
Поскольку ваше состояние включает в себя сообщения, флаг загрузки и ошибку, подписка на состояние в целом приведет к запуску вашей функции подписки или асинхронного канала независимо от того, какие из этих изменений. Если вы создаете методы селектора с помощью @Selector() в своем классе состояний, вы можете подписаться на них для большей детализации, например, чтобы реагировать только на флаг загрузки.
Ответ кажется довольно тяжелым и включает в себя настройку перехватчика для вашего приложения. Тогда к нему будут всплывать ошибки.
Если вы не готовы к созданию этой установки полного цикла, вы можете сделать облегченную версию, сообщив статус ответа «срез».resStatus
.
export interface IReqStatus {
loading?: boolean;
error?: any;
success?: boolean;
action?: string; <-- optionally send along the action this belongs to
}
Пример действия, выполняющего запрос к конечной точке 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));
})
);
}
Используйте действие для управления статусом ответа
@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);
}
В моем случае сервер отвечает свойством статуса успеха. Но если запрос полностью нарушен, catchError должен обновитьreqStatus
.
Вы можете просматривать статусы ваших запросов с помощьюstate.reqStatus
в любом месте. И, надеюсь, вы даже сможете сохранитьasync pipe
в такте, как:
<button [loading]="$any(reqStatus$ | async).loading" ...>