NgRx 特征创建器
封面照片由Sigmund拍摄,来自 Unsplash。
该createFeature函数在 NgRx v12.1 中引入。
它通过为每个要素状态属性生成要素选择器及其子选择器,从而减少选择器文件中的重复代码。其设计灵感来源于ngrx-child-selectors库。
NgRx 功能
全局状态管理主要由三个基本构建模块组成@ngrx/store:actions、reducer 和 selector。对于特定的特性状态,我们会创建一个 reducer 来处理基于已分发 action 的状态转换,并使用 selector 来获取特性状态的切片。此外,我们还需要定义一个特性名称,以便在 NgRx store 中注册特性 reducer。因此,我们可以将 NgRx 特性视为特定特性状态的特性名称、特性 reducer 和 selector 的组合。接下来,我们来看看创建 NgRx 特性的“传统”方法。
要创建 reducer,可以使用createReducer该@ngrx/store包中的函数:
// books.reducer.ts
import { createReducer } from "@ngrx/store";
import * as BookListPageActions from "./book-list-page.actions";
import * as BooksApiActions from "./books-api.actions";
export const featureName = "books";
export interface State {
books: Book[];
loading: boolean;
}
const initialState: State = {
books: [],
loading: false,
};
export const reducer = createReducer(
initialState,
on(BookListPageActions.enter, (state) => ({
...state,
loading: true,
})),
on(BooksApiActions.loadBooksSuccess, (state, { books }) => ({
...state,
books,
loading: false,
}))
);
要将此 reducer 注册到 NgRx store 中,我们使用以下StoreModule.forFeature方法:
// books.module.ts
import { StoreModule } from "@ngrx/store";
import * as fromBooks from "./books.reducer";
@NgModule({
imports: [
StoreModule.forFeature(fromBooks.featureName, fromBooks.reducer),
],
})
export class BooksModule {}
为了从数据存储中选择状态,我们创建一个特性选择器、子选择器以及一个视图模型选择器:
// books.selectors.ts
import { createFeatureSelector, createSelector } from "@ngrx/store";
import * as fromBooks from "./books.reducer";
// feature selector
export const selectBooksState = createFeatureSelector<fromBooks.State>(
fromBooks.featureKey
);
// child selectors
export const selectBooks = createSelector(
selectBooksState,
(state) => state.books
);
export const selectLoading = createSelector(
selectBooksState,
(state) => state.loading
);
// view model selector
export const selectBookListPageViewModel = createSelector(
selectBooks,
selectLoading,
(books, loading) => ({ books, loading })
);
使用功能创建器
现在我们来看看如何使用该createFeature函数实现同样的结果。首先,我们将重构 reducer 文件:
// `createFeature` is imported from `@ngrx/store`
import { createFeature, createReducer } from "@ngrx/store";
import * as BookListPageActions from "./book-list-page.actions";
import * as BooksApiActions from "./books-api.actions";
interface State {
books: Book[];
loading: boolean;
}
const initialState: State = {
books: [],
loading: false,
};
// feature name and reducer are now passed to `createFeature`
export const booksFeature = createFeature({
name: "books",
reducer: createReducer(
initialState,
on(BookListPageActions.enter, (state) => ({
...state,
loading: true,
})),
on(BooksApiActions.loadBooksSuccess, (state, { books }) => ({
...state,
books,
loading: false,
}))
),
});
现在可以通过将整个特征对象传递给该StoreModule.forFeature方法,在 store 中注册特征 reducer:
// books.module.ts
import { StoreModule } from "@ngrx/store";
import { booksFeature } from "./books.reducer";
@NgModule({
imports: [StoreModule.forFeature(booksFeature)],
})
export class BooksModule {}
最后,我们来看看选择器文件长什么样:
// books.selectors.ts
import { createSelector } from "@ngrx/store";
import { booksFeature } from "./books.reducer";
export const selectBookListPageViewModel = createSelector(
booksFeature.selectBooks,
booksFeature.selectLoading,
(books, loading) => ({ books, loading })
);
之前手动创建的要素选择器和子选择器现已移除,因为createFeature系统会自动生成它们。所有生成的选择器都带有“select”前缀,要素选择器带有“State”后缀。
在这个例子中,特征选择器的名称是 `<feature selector>` selectBooksState,其中“books”是特征名称。子选择器的名称selectBooks分别是 `<child selector>` 和selectLoading`<child selector>`,它们分别基于书籍特征状态的属性名称。
结论
特性创建者可以利用 TypeScript v4.1 中引入的模板字面量类型来减少选择器文件中的重复代码。这可以极大地改进你的代码,尤其是在处理大型特性状态时。
资源
同行评审员
谢谢 Tim 为我这篇文章提出的宝贵建议!
文章来源:https://dev.to/this-is-angular/ngrx-feature-creator-2c72《NgRx 功能创建器》指南现已发布在 NgRx 官方文档中。点击此处了解更多信息。