我想答案有点晚了,但还是:D
首先,我可以立即摆脱这三个动作——LoadingStart、LoadingFinish、LoadingError。我不确定你是否一定会从中获利,只是因为 NGXS 会为你返回一个错误给onError 回调。
但这也取决于您所说的“加载”,它适用于所有请求还是仅适用于“文章”?如果只是“文章” - 创建一个名为 loading 的状态属性要容易得多,例如:
@State({
name: 'articles',
defaults: {
loading: false,
articles: []
}
})
export class ArticlesState {}
由于 NGXS 可以执行异步工作,因此在您返回 Promise 或 Observable 的情况下,最好应用 finalize 运算符,它会在 observable 完成或错误时调用函数,因此您可能不会需要LoadingFinish和LoadingError。
第二个问题是你剩下的 5 个动作。这也取决于您所说的“样板”,基本上您不能将 1 个动作用于 5 个动作:D 除非它是一些通用动作,否则看起来像这样:
// Let's assume that it's some shared
// `enum` that's used in multiple places
// by multiple states
export const enum CrudOperation {
GetAll,
GetById,
Delete,
Update,
Create
}
export class ArticlesCrudAction {
public static readonly type = '[Articles] Crud action';
constructor(public operation: CrudOperation, public article?: Article) {}
}
因此,如果您想删除一篇文章 - 您将发送这样的操作:
public deleteArticle(article: Article): void {
this.store.dispatch(
new ArticlesCrudAction(CrudOperation.Delete, article)
);
}
这只是一个伪代码,但我不喜欢这种方法,因为我之前已经看到过。显式优于隐式。您需要写一些switch-case 并确定要使用的服务方法。
如果我是你 - 我仍然会保留这些 CRUD 操作,因为它们可以让正在发生的事情变得清晰。最终代码如下所示:
function setLoading(loading: boolean) {
return (state: Readonly<ArticlesStateModel>) => ({ ...state, loading });
}
function startLoading() {
return setLoading(true);
}
function stopLoading() {
return setLoading(false);
}
@State<ArticlesStateModel>({
name: 'articles',
defaults: {
loading: false,
articles: []
}
})
export class ArticlesState {
@Selector()
public static isLoading(state: ArticlesStateModel): boolean {
return state.loading;
}
@Selector()
public static getArticles(state: ArticlesStateModel): Article[] {
return state.articles;
}
constructor(private articlesService: ArticlesService) {}
@Action(GetAllArticles)
public getAllArticles(ctx: StateContext<ArticlesStateModel>) {
return this.request(
ctx,
() => this.articlesService.getAllArticles(),
articles => ctx.patchState({ articles })
);
}
@Action(GetArticleById)
public getArticleById(ctx: StateContext<ArticlesStateModel>, { id }: GetArticleById) {
return this.request(ctx, () => this.articlesService.getArticleById(id));
}
@Action(DeleteArticle)
public deleteArticle(ctx: StateContext<ArticlesStateModel>, { article }: DeleteArticle) {
return this.request(ctx, () => this.articlesService.deleteArticle(article.id));
}
@Action(UpdateArticle)
public updateArticle(ctx: StateContext<ArticlesStateModel>, { article }: UpdateArticle) {
return this.request(ctx, () => this.articlesService.updateArticle(article));
}
@Action(CreateArticle)
public createArticle(ctx: StateContext<ArticlesStateModel>, { article }: CreateArticle) {
return this.request(ctx, () => this.articlesService.createArticle(article));
}
private request<T>(
ctx: StateContext<ArticlesStateModel>,
request: () => Observable<T>,
callback?: (response: T) => void
) {
ctx.setState(startLoading());
return request().pipe(
tap(response => callback && callback(response)),
finalize(() => ctx.setState(stopLoading()))
);
}
}
您还可以创建一个附加方法来修补您的状态,在发送请求之前将loading 属性设置为true,并使用finalize 进行管道传输。
这仍然非常抽象,取决于您的实现。
我们还在3.4.0 版本中引入了state operators,它可以通过引入声明性来减少您的代码。