Use ngrx/store and Reducers for Angular Application State

John Lindquist
InstructorJohn Lindquist

Share this video with your friends

Send Tweet
Published 6 years ago
Updated 3 years ago

ngrx/store is a library that simplifies common RxJS patterns for managing state and gives you an easy api to use it within your Angular application. This lesson shows how to convert a common startWith and scan stream into an ngrx Store and reducer.

[00:00] What we've done here with start with in scan is actually a very, very common pattern where you have an initial value, and then you want to change the value based on some other values that come through, and you want to keep track of it.

[00:13] There is a library that can help us with this, so we don't manage all of this ourselves, and we can move all of this into a different file. We're going to come over here. I'm going to create in my source a new filed called "Reducers."

[00:26] In my reducers, I'm going to set a function called "Clock." This is going to be our clock reducer. Clock takes a state. Instead of using "Start with," we'll set an initial value of "New date." Then for now I'm going to return the state.

[00:49] If this function is called, it's going to set this state to new date and return the state. To use this reducer or this clock function, we need to actually bootstrap it into our application, which means I need to add something to the list of my dependencies here.

[01:05] My dependencies are an array after that component, and I'm going to say, "Provide store." Where provide store is going to come from is from NGRX/Store. I'll go ahead and provide store. The store that I want to provide is an object with key value pairs to those reducers.

[01:27] I want to get clock, and I need to import clock. I'll import from reducers and I'll import clock. We've hooked this up where we have this store that uses the clock reducer. This is all basically configuration stuff.

[01:46] Get used to it, and don't worry too much about it other than passing the reducers into this object and then providing them to the store. Then that gives you immediate access after you import store from MGRX.

[02:04] We can use angular twos, dependency injection to grab the store using a type. Our clock instead of this big stream can be store, select, and then a string of clock to grab whatever comes from that clock reducer.

[02:19] I'll hit save. I'll refresh, and then you can see we have this new date and it's frozen at that time that the reducer was called and the new date was set. With this stream, I'm going to get rid of everything and start with a scan. I'm going to move this over to my reducers.

[02:40] Instead of doing these if statements, I'm actually going to switch on an action type. We'll have actions come through and actions have a -- I'm going to de-structure this -- a type property on them. Type in here is what I can pass along. Instead of to start with in scan, I'm going to subscribe to this stream.

[03:08] The type is what comes in, and I'm going to say, "Store.dispatch an object with that type on it." We're going to take this type of hour, this type of second, and it gets pushed into subscribe, and gets dispatched to the store, which gets handled in the reducer.

[03:26] We have the type here where I can switch on the type. If the type is second, we want to do this and return date. If the type is hour, we want to do this and return the date. We'll delete all of these. We've refactored this into something that can, when I refresh...

[04:01] You can see I forgot to rename my accumulator to state. They're basically the same thing. I'll hit save again. I'll refresh. You can now see the time is ticking away one second. When I click update, it's going by each hour.

[04:18] To review that, what's going on is in our subscribe, we're getting a type whether it's hour when I click or second when one second passes by. Then we're saying store dispatch an object with the type property on it that's set to either hour a second. That comes through our reducer.

[04:37] Our reducer was configured here where we say, "Grab the reducer, hook it into our store." so that when it comes through this reducer the first time, the state is set to a new date. Then from then on out, if a type comes through, we say, "Is it a second? Then do this logic. Is it an hour? Then do this logic in return."

[04:59] We have that same configuration as if we're doing a start with and a scan. It's that now we can extract all of that logic, and make it reusable and portable through this clock reducer function.

Nathan Brenner
Nathan Brenner
~ 6 years ago

This works, and it matches the current docs:

import { StoreModule } from '@ngrx/store';
@NgModule({
    imports: [ StoreModule.provideStore({ clock: clock }, { clock: new Date() }) 

The first parameter to the provideStore method is the reducer function, the second is the initialized state.

Doug
Doug
~ 6 years ago

I'm still getting an error in ng2:

ERROR in [default] /Users/dougboyd/look/learn-rxjs/src/app/app.component.ts:21:21 
Generic type 'Store<T>' requires 1 type argument(s).
Child html-webpack-plugin for "index.html":
Jon
Jon
~ 6 years ago

I am too. Seems to be a recurring pattern with these courses..Angular2/RxJs is changing too quickly for EggHead to keep up.

James
James
~ 6 years ago

It looks like you have to install ngrx separately

step 1 - install from here https://github.com/ngrx/store

npm install @ngrx/core @ngrx/store --save

step 2 - add to your module

import { clock } from './reducers'
import { StoreModule } from 'ngrx/store'
  
@Ngmodule({
  imports: [ // NOT bootstrap like in the demo
    StoreModule.provideStore({ clock })
 // ... the rest of your module

step 3 - add to your component

import { Store } from '@ngrx/store'

export class AppComponent {
  click$ = new Subject()
  clock
  constructor(store: Store<any>) { // either use `any` type or swap in your class interface for typescript
    this.clock = store.select('clock')
// ...

step 4 - add your .reducers.ts

export const clock = (state: Date = new Date(), {type}) => {
  const date = new Date(state.getTime());
  switch (type) {
    case 'second':
      date.setSeconds(date.getSeconds() + 1);
      return date;
    case 'hour':
      date.setHours(date.getHours() + 1);
      return date;
  }
  return state;
}

Hope this helps!

Omar Qaddoumi
Omar Qaddoumi
~ 5 years ago

Ok, so the comments above helped me figure out my issue. I'm building this on stackblitz.com using the latest Angular and rxjs and @ngrx/store.

rxjs: 5.5.2  
core-js: 2.5.1  
@angular/core: 5.0.0  
@ngrx/store: 4.1.1  

And what I needed to do was to follow the advice of not setting this up in my main.ts file, but instead, setup the following in my app.module:

import { StoreModule } from '@ngrx/store';  
import { clock } from './reducers/reducers';  

imports:      [ 
    BrowserModule, 
    FormsModule,
    StoreModule.forRoot({clock})
  ],

thanks for the help, guys; as you mentioned, this stuff is changing faster than egghead can keep up. I think these few comments in the discussion will hope most that get stuck.

Aljosha Novakovic
Aljosha Novakovic
~ 3 years ago

What's the deal with this format of "bootstrapping" it into angular? I guess this is how setup was 2 or 3 years ago? For a paid course I would expect that simple updates can be made on the videos... I'm finding myself using free youtube courses for learning ngrx more so far...