想象一下,我的商店中有两个子状态,每个子状态一个: 1。用户 2。文章
这两个都支持CRUD(创建,读取,更新删除)。
要做到这一点,我必须对它们两个进行至少7个动作。
文章操作列表为例
1. GetAllArticle
2. GetArticleById
3. DeleteArticle
4. UpdateArticle
5. CreateArticle
6. LoadingStart
7. LoadingFinish
8. LoadingError
将为用户以及其减速器/动作处理程序创建所有8个动作。这会为大量非常平凡而琐碎的事情带来大量的样板。
我的问题是,是否有办法减少样板和代码重复?
答案 0 :(得分:1)
我想答案有点晚了,但是:D
首先,我可以立即摆脱三个动作-LoadingStart
,LoadingFinish
,LoadingError
。我不确定您一定会从中获利,仅仅是因为NGXS会为您向onError
回调返回错误。
但这还取决于您所说的“加载”,是应用于所有请求还是仅应用于“文章”?如果仅是“文章”,那么创建一个名为loading
的状态属性要容易得多,类似于:
@State({
name: 'articles',
defaults: {
loading: false,
articles: []
}
})
export class ArticlesState {}
由于NGXS可以执行异步工作,因此在返回Promise
或Observable
的情况下-最好应用finalize
运算符,该运算符将在可观察的完成或结束时调用一个函数。错误,因此您可能不需要LoadingFinish
和LoadingError
。
第二个问题是您剩余的5个动作。这也取决于您所谓的“样板”,基本上您不能对5个动作使用1个动作: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,这可能会由于声明性而减少代码。