Redux 101

INTRODUCTION TO REDUX ARCHITECTURE

Agenda

  • Learn

    • What is application state?
    • What is Redux?
    • How to write code with Redux?
  • Build

    • Live coding
    • Exercise
  • Further learning

What is an application state?

All the data that an application needs at given moment:
  • To display UI of given structure and content (User Interactions and Input)
  • To perform some actions (Authentication Tokens)
  • To provide behaviour persistency (Server Responses)

What is Redux?

  • Simple, reliable and scalable application architecture (e.g. Facebook)
  • Small set of building blocks used to manage application state
  • A way of thinking about where to put business logic

Three Principles of Redux

  • Single Source of Truth
  • State is read only
  • Changes are made with pure functions (i.e. reducers)

General Redux Schema

Simple Example

Codepen

Live Coding Session

Step 1

  • Install @ngrx/store package
  • Write your first reducer, hint: (State, Action) => State
  • Add initial, default state to your reducer
  • Wire up the reducer with the module using StoreModule.forRoot method and ActionReducerMap

Step 2

  • Inject Store to the BooksShelfComponent
  • Use the store to render books in BooksShelfComponent:
    • Stop using the service in getData
    • Fetch all books from the store using select operator,
    • Temporarily subscribe to the store and assign them to the existing books field
    • Map the book collection to a filtered one, based on this.mode (if set)

Step 3

  • Refactor BooksShelfComponent to use | async instead of subscribing to books
  • Extract books selection logic to store/selectors.ts, use createSelector function,
  • Write a unit test for your reducer

Step 4 (1 of 2)

  • Implement UpdateBook action that will be dispatched when e.g. moving a book
  • Handle updating a book in the reducer:
    • Notice that there's no invariant property, so introduce id
    • Assign unique id to a NgrxBook, use uuid package
    • Handle an update action that carries an id

Step 4 (2 of 2)

  • Test the reducer (think of it as comparing state after passing an action with expected state)
  • Dispatch the update action on book move (BookShelfComponent)

Redux Schema With Effects

Step 5 (1 of 2)

  • Remove unused methods from ShelfService
  • Add ShelfService.fetchBooks method that will return books with some delay, to emulate the HTTP request behaviour (maybe use timer from rxjs)
  • Be sure that ShelfService returns other titles than in the default state
  • Install @ngrx/effects package

Step 5 (2 of 2)

  • Add LoadBooks and BooksLoaded actions, handle them in the reducer (add loading: boolean state)
  • Write an effect that reloads books on LoadBooks action and emits BooksLoaded action
  • Register the effect with EffectsModule.forRoot
  • Add a button that will dispatch LoadBooks action

Excercise 1

Implement adding new book:

  • Create AddBook action
  • Handle the action in the reducer
  • Dispatch the action in BooksShelfComponent

Step 6

  • Install @ngrx/store-devtools
  • Install in your web browser the Redux Devtools Extension
  • Wire-up StoreDevToolsModule.instrument({ maxAge: 25 }) with your module

Step 7 (1 of 2)

  • Install @ngrx/router-store package
  • Extend NgrxModuleState to have router key of type RouterReducerState - the RouterStateUrl was implemented for you in store/router-store.ts
  • Import and register routerReducer in your present ActionReducerMap (suggested key: router)

Step 7 (2 of 2)

  • Wire up StoreRouterConnectingModule.forRoot({ stateKey: 'router' }) with your module
  • Add { provide: RouterStateSerializer, useClass: CustomSerializer } to your module providers. CustomSerializer is implemented in store/router-store.ts.

Step 8

  • Go to your reducer and clear items in the initial state,
  • Go to books-shelf.component.html and hide div.collection_container when books are not loaded with *ngIf
  • Add there a notification that books are loading

Excercise 2

  • Add an Effect to your effects class, that will start fetching books when ROUTER_NAVIGATION action appears
  • Write selectors that will help you get store.router.state.params.collection that tells which shelf is selected.
  • Use these selectors to set mode in BooksShelfComponent.

Excercise 3

  • Use the effect from Ex2 to pass collection from router store to ShelfService.getData
  • Adjust ShelfService.getData to filter out books depending on the collection (optional argument)
  • Hide shelves in BooksShelfComponent based on the value of mode$ (collection).
  • Remove data filtering data logic from BooksShelfComponent

Final Step

  • Install jasmine-marbles
  • Write a hot marble/observable that will emulate a stream of actions
  • Write a cold marble/observable that will emulate the behaviour of the effect
  • Compare them using expect(...).toBeObservable(...)

Learn more

Thank you