NgRx Entities: Updating the selectors

Note: This will be a series of posts around how I moved from custom built reducers and state management with NgRx to use the NgRx Entity library.


In NgRx Entities: Namespaces with TypeScript I previously wrote how I was changing up my reducers to use the Entity library from NgRx and utilizing TypeScript to my advantage. The next step was to update the selectors I had setup to use the new namespace and adapter.

A great reference book that goes deeper into this topic: Architecting Angular Applications with Redux, RxJS, and NgRx

What was there before

Again, still as an example with using books, here is a snippet code of what the selector file looked like.

1
// store/selectors/books.selectors.ts
2
3
const selectBooksState = (state: AppState) => state.books;
4
5
export const selectBooks = createSelector(
6
  selectBooksState,
7
  (state: BooksState) => state.books,
8
);
9
10
export const selectSelectedBook = createSelector(
11
  selectBooksState,
12
  (state: BooksState) => state.selectedBook,
13
);

Here we have two selectors:

  1. selectBooks which will return the full collection of the books in the store
  2. selectSelectedBook which returns the individual selected book from the store of books

Moving to the createFeatureSelector

Since the entity use with NgRx creates a more complex store object, moving to the createFeatureSelector needed to happen. This method from the NgRx store library creates a specific key on the store for your feature so that an optimization happens during the selection process.

1
// store/selectors/books.selectors.ts
2
3
export const selectBooksState
4
  = createFeatureSelector<BooksStore.State>('books');
5

With this in place, the selector can access the store quicker based on the shape created by the entity setup:

1
// store shape
2
{
3
  books: {
4
    ids: [1, 2, ...],
5
    entities: [
6
      { id: 1, title: 'The Shinning' },
7
      { id: 2, title: 'It' },
8
      ...
9
    ],
10
    selectedBook: null,
11
  }
12
}

Now I can change up the rest of the selectors to use the adapter selectors exported from the BooksStore that was exported and also change the selectSelectedBook to look into the entities array.

1
// store/selectors/books.selectors.ts
2
3
export const selectBooks = createSelector(
4
  selectBooksState,
5
  BooksStore.selectAll,
6
);
7
8
export const selectSelectedBook
9
  = (bookId: number) => createSelector(
10
    selectBooksState,
11
    (state: BooksStore.State) => state.entities[bookId],
12
);

Again, there tends to be extra code with this move, but in the long run, the changes made to the reducers make up for it all, which I will dive into next.

Filed under: Code