Follow

Follow
NgRx Workshop: Part 3 - Reducers

NgRx Workshop: Part 3 - Reducers

Alfredo Perez's photo
Alfredo Perez
·Apr 2, 2020·

2 min read

  • 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 feature

  • Declares an initialState that included the initial state

  • Declare 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>[] = [];
 
Share this