Produce new states
Receive the last state and next action.
Switch on the action type
Use pure, immutable operations
Notes
Create separate reducer per feature
Should live outside the feature because the state it is shared in the whole application
State should save the model as it comes from the API. This can be later transformed into the selectors.
Combine actions that can use the same reducer
on(BooksPageActions.enter, BooksPageActions.clearSelectedBook, (state, action) => ({
...state,
activeBookId: null
})),
Only reducers can modify state and it should do it in an immutable way
Create helper functions to handle the data manipulation of collections.
Example
const createBook = (books: BookModel[], book: BookModel) => [...books, book];
const updateBook = (books: BookModel[], changes: BookModel) =>
books.map(book => {
return book.id === changes.id ? Object.assign({}, book, changes) : book;
});
const deleteBook = (books: BookModel[], bookId: string) =>
books.filter(book => bookId !== book.id);
...
on(BooksApiActions.bookCreated, (state, action) => {
return {
collection: createBook(state.collection, action.book),
activeBookId: null
};
}),
on(BooksApiActions.bookUpdated, (state, action) => {
return {
collection: updateBook(state.collection, action.book),
activeBookId: null
};
}),
on(BooksApiActions.bookDeleted, (state, action) => {
return {
...state,
collection: deleteBook(state.collection, action.bookId)
};
})
Feature reducer file
Declares a
State
interface with the state for the featureDeclares an
initialState
that included the initial stateDeclare a feature reducer that contains the result of using
createReducer
Exports a
reducer
function that wraps the reducer created. This is needed for AOT and it is not needed when using Ivy.
Example
export interface State {
collection: BookModel[];
activeBookId: string | null;
}
export const initialState: State = {
collection: [],
activeBookId: null
};
export const booksReducer = createReducer(
initialState,
on(BooksPageActions.enter,
BooksPageActions.clearSelectedBook, (state, action) => ({
...state,
activeBookId: null
})),
on(BooksPageActions.selectBook, (state, action) => ({
...state,
activeBookId: action.bookId
})),
);
export function reducer(state: State | undefined, action: Action) {
return booksReducer(state, action);
}
- The index file defines the state and assigns each reducer to a property on the state
import * as fromBooks from "./books.reducer";
export interface State {
books: fromBooks.State;
}
export const reducers: ActionReducerMap<State> = {
books:fromBooks.reducer
};
export const metaReducers: MetaReducer<State>[] = [];