Skip to content

Commit 2b53495

Browse files
docs(store): add feature creator guide (#3202)
* docs(store): add feature creator guide * apply Brandon's suggestions * apply Tim's CR suggestions
1 parent 5974913 commit 2b53495

File tree

2 files changed

+127
-0
lines changed

2 files changed

+127
-0
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Feature Creators
2+
3+
## What is an NgRx feature?
4+
5+
There are three main building blocks of global state management with `@ngrx/store`: actions, reducers, and selectors.
6+
For a particular feature state, we create a reducer for handling state transitions based on the dispatched actions
7+
and selectors to obtain slices of the feature state. Also, we need to define a feature name needed to register
8+
the feature reducer in the NgRx store. Therefore, we can consider the NgRx feature as a grouping of the feature name,
9+
feature reducer, and selectors for the particular feature state.
10+
11+
## Using feature creator
12+
13+
The `createFeature` function reduces repetitive code in selector files by generating a feature selector and child selectors
14+
for each feature state property. It accepts an object containing a feature name and a feature reducer as the input argument:
15+
16+
<code-example header="books.reducer.ts">
17+
import { createFeature, createReducer } from '@ngrx/store';
18+
import { Book } from './book.model';
19+
20+
import * as BookListPageActions from './book-list-page.actions';
21+
import * as BooksApiActions from './books-api.actions';
22+
23+
interface State {
24+
books: Book[];
25+
loading: boolean;
26+
}
27+
28+
const initialState: State = {
29+
books: [],
30+
loading: false,
31+
};
32+
33+
export const booksFeature = createFeature({
34+
name: 'books',
35+
reducer: createReducer(
36+
initialState,
37+
on(BookListPageActions.enter, (state) => ({
38+
...state,
39+
loading: true,
40+
})),
41+
on(BooksApiActions.loadBooksSuccess, (state, { books }) => ({
42+
...state,
43+
books,
44+
loading: false,
45+
}))
46+
),
47+
});
48+
49+
export const {
50+
name, // feature name
51+
reducer, // feature reducer
52+
selectBooksState, // feature selector
53+
selectBooks, // selector for `books` property
54+
selectLoading, // selector for `loading` property
55+
} = booksFeature;
56+
</code-example>
57+
58+
An object created with the `createFeature` function contains a feature name, a feature reducer, a feature selector,
59+
and a selector for each feature state property. All generated selectors have the "select" prefix, and the feature selector has
60+
the "State" suffix. In this example, the name of the feature selector is `selectBooksState`, where "books" is the feature name.
61+
The names of the child selectors are `selectBooks` and `selectLoading`, based on the property names of the books feature state.
62+
63+
The generated selectors can be used independently or to create other selectors:
64+
65+
<code-example header="books.selectors.ts">
66+
import { createSelector } from '@ngrx/store';
67+
import { booksFeature } from './books.reducer';
68+
69+
export const selectBookListPageViewModel = createSelector(
70+
booksFeature.selectBooks,
71+
booksFeature.selectLoading,
72+
(books, loading) => ({ books, loading })
73+
);
74+
</code-example>
75+
76+
## Feature registration
77+
78+
Registering the feature reducer in the store can be done by passing the entire feature object to the `StoreModule.forFeature` method:
79+
80+
<code-example header="books.module.ts">
81+
import { NgModule } from '@angular/core';
82+
import { StoreModule } from '@ngrx/store';
83+
import { booksFeature } from './books.reducer';
84+
85+
@NgModule({
86+
imports: [StoreModule.forFeature(booksFeature)],
87+
})
88+
export class BooksModule {}
89+
</code-example>
90+
91+
## Restrictions
92+
93+
The `createFeature` function cannot be used for features whose state contains optional properties.
94+
In other words, all state properties have to be passed to the initial state object.
95+
96+
So, if the state contains optional properties:
97+
98+
<code-example header="books.reducer.ts">
99+
interface State {
100+
books: Book[];
101+
activeBookId?: string;
102+
}
103+
104+
const initialState: State = {
105+
books: [],
106+
};
107+
</code-example>
108+
109+
Each optional symbol (`?`) have to be replaced with `| null` or `| undefined`:
110+
111+
<code-example header="books.reducer.ts">
112+
interface State {
113+
books: Book[];
114+
activeBookId: string | null;
115+
// or activeBookId: string | undefined;
116+
}
117+
118+
const initialState: State = {
119+
books: [],
120+
activeBookId: null,
121+
// or activeBookId: undefined,
122+
};
123+
</code-example>

projects/ngrx.io/content/navigation.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@
110110
{
111111
"title": "Meta-Reducers",
112112
"url": "guide/store/metareducers"
113+
},
114+
{
115+
"title": "Feature Creators",
116+
"url": "guide/store/feature-creators"
113117
}
114118
]
115119
},

0 commit comments

Comments
 (0)