I’ve talked around NgRX several times over the last few months but I’ve yet to write an article on how to use NgRX. This is because I was under the impression that there were better articles available. And while there are a lot of articles available, there is not anything I feel comfortable sending another programmer to who is trying to learn NgRX.
So, rather than spend a lot of time on why you want to learn NgRX, or arguing against some of the unfounded hesitations, today I’m just going to dive into how to use NgRX.
For the purposes of this post, I’m going to assume that you already have a basic project started using the Angular CLI. For this post, I’m using version 1.0.1, but the steps should work the same for 1.0.0 as well.
You will also need to install NgRX.
npm install --save-dev @ngrx/store @ngrx/core @ngrx/effects
The Four Components
The NgRX model is composed of four basic elements: Stores, Reducers, Actions, and Effects. Central to all of this is the store. Let’s start there.
The best way to think of a store is as a database made up of several tables. While the store doesn’t have any of the fancy stuff associated with a database, it is a collection of smaller elements that most closely align with tables in a database. The analogy isn’t perfect, but it is a start. I’ve found, this analogy clears up a lot of confusion for beginners.
Currently, we define our Store at the app.module level. Most of the articles you will find online about how to use NgRX will have you defining the store in app.module.ts. While this will work, in my own code, I create a separate module in a file named app.store.ts and import that module into app.module. This keeps both my store definition code clean and isolated as well as keeping my app.module code clean. Mixing concerns might make a great desert, but it is a terrible coding practice.
What follows is typically how I structure my store module, which we will import into our app module.
This varies a bit from what most of the other guys are teaching, so let me explain a bit.
I create the const reducers outside of the @NgModule definition because I want to be able to use that object in the @NgModule section and in the class, which we will talk about soon.
Next, we want to import this module into our main app.module.ts file
Next in our list of files that we need to make NgRX work, are reducers. The job of a reducer is to mutate the “table” in our store. What is unique about reducers is that they are functions, not classes.
A generic reducer that doesn’t do a whole lot would look like this:
And now that we have a reducer, we need to add it to our app.store reducer const
Now that we have a reducer defined, we can subscribe to the “table” it represents in our store by selecting it from our store. Most of the code you will see describing how to do this will look like this.
The problem with this, is that it is prone to error. You could type in a string that looks something like what you want, but is off by a character. I do that more often that I’d like to admit. And strings don’t give you any intellisense. We can fix both of those problems by giving our AppStores class static properties that return the string we are looking for. This problem is commonly referred to as a “magic string” and I’ve written more about why this is bad in my article “Magic Strings and Magic Numbers”
Now, you might think that all we are going to do is to create a field that returns the string we are looking for, but there is a chance, however small, that we could type that wrong. So, we need to embellish the code a bit.
In the code above, you’ll see that we created a property named “generic” that passes
AppStores.checkForName. What, exactly, is this doing?
Well, if you look at the code at line 24, you’ll see that we’ve created another static property called “stores”. The first time it is called, it creates a map of properties to property names that it stores in the private field name _store. Now, if you access a property name “generic” it returns the string, “generic”.
We then pass the string into
checkForName to make sure that property really exists. You could possibly mistype it, but the bigger save here is if you remove the reducer but forget to remove the associated property.
Now that we have this setup, we can replace
So far, all we’ve been able to do is to establish a “table” in a store and listen to is. But, we have no way of actually changing the data. This is where Actions come in.
Right now, our “generic” will return 0 every time it is called. If we wanted to, we could create an action that will add one to generic every time the action is fired. To make this more flexible, we can pass in how much to add. Let’s create our first action.
an action is just an object with a type property and a payload property. The type property is a string that describes what it is we want to do and the payload property is the data we want to use when that action is performed.
As a way of eliminating magic strings and redundancy, we setup our actions to look similar to this:
You will notice that everything is static and everything is public. If you’ve read other articles, you will also notice that this is a bit different from how others might suggest that you set this up. Others suggest that you make this class injectable and that you inject it into the class you want to use.
The reason I don’t is because injecting this into another class doesn’t add any value and actually complicates our code. The reason you use dependency injection in the first place is so that you can replace out one object with another. This is so you can mock out calls. But Actions, by definition, provide no functionality. They are simply convenience classes. As I said, they eliminate redundancy and eliminate magic strings. Why would you ever want to replace them? If you are doing more than assigning data to your payload, you are doing something wrong.
One of the things I like about NgRX is that by using it, I reduce the number of parameters that need to be injected into my class. By only needing to inject the store, rather than multiple services, and in this case even multiple Actions, my code becomes incredibly easy to maintain.
OK. Enough with the architecture rant. How do we use this action?
As I just mentioned, we inject the store into the class, which you should have already done when you subscribed to the store’s “table.” This store object also has a dispatch function. You dispatch the action object that gets returned and it gets sent off to the Reducer that should deal with it.
Now, let’s handle it in our reducer.
Remember, our reducer is just a basic function with a switch statement. You can see that we access our static constant value “ADD” and added the payload to the existing state.
Now, it might be just a bit confusing here because when you look at the function parameters, you see that the default value for stat is zero. But that is only true the first time this is run, which for our purposes is when the application is loaded. Every time an action is fired, the state parameter has the current state from the last mutation. So, our ADD switch is always adding to the most recent state and returning the value, which becomes our new current state.
Now, that’s the basics of NgRX. But the question that remains to be answered is, “Where do I put my code to retrieve data?”
This is where Effects come in. Think of Effects as “Side effects.” What we have so far is very predictable. Give a specific state, an action with a specific payload, the result will always be the same. Very predictable. Extremely testable. If you add into this mix retrieving data all of that predictability goes away and we go back to needing mocks and injectables to test our logic. Not something I’m a huge fan of doing. So let’s leave the database access code out of the basic framework and instead put them in our Effects.
A basic Effect would look something like this.
Note, the constructor is where we inject specific objects that we will need for our code. Each field decorated with @Effect is a separate observable that processes the logic. This is typically used to get data from the database, but I’ve started considering how this might be put to use for things such as processing business rules.
You will also notice that the last line of an @Effect is an action. This action is automatically dispatched to the store and processed by the appropriate reducer. This is how the results of our ajax call make their way back to the store.
Again, I’ve deviated a bit from other articles you may have read. Typically, you’ll see this code as calling out to a service which then calls out to the database. I’ve put the Http code in the Effect. I think for simple calls, this is fine. If you really have a lot of setup to do prior to making a call a service might make more sense.
Finally, we’ll want to register this code in our app.stores.ts file.
This is a step I tend to forget and then I’m tearing my hair out trying to figure out why it never gets called.
Before I end, I want to talk a bit about how to organize the code.
When I started with NgRX, I thought the best organization would be to have a directory for Actions, Reducers, Effects, and maybe one called Model where I keep my interface definitions for the stores (we’ll talk about those in a future post). But, one of the most common criticisms of NgRX or Redux in general is the fact that you need so many files to get anything done. Scattering them between directories doesn’t help.
So, I’ve since moved to putting all of the files related to a reducer under their own directory. Similar to how you would put the html, css, spec and ts files together for a component. This seems to make the code much more manageable.
In future posts, I will demonstrate how to use this pattern not just for a simple CRUD application. You can find those in other articles. But, also for error handling, wait states, and more. Don’t forget to sign up for our newsletter if you haven’t already. Use the box at the top of this screen.
Other post in Angular 2
- Angular 2 – First Impressions [Compared to Angular 1] - February 25th, 2016
- Angular 2 Thoughts - October 4th, 2016
- Getting Started with Angular 2 - October 25th, 2016
- Unit Testing an Angular 2 CLI Project - November 22nd, 2016
- Adding Client Side Routing to Angular 2 - November 29th, 2016
- Angular 2 Lazy Loading - December 6th, 2016
- Reasons to use RxJS Today - December 13th, 2016
- Dissecting Angular 2 Modules - December 20th, 2016
- Awesome Angular2 Architecture Options and Opinions - December 27th, 2016
- What if Everything Was Immutable? - January 10th, 2017
- Amazing Angular2 DOM Tips, Tricks, and Warnings - January 17th, 2017
- Secrets to Styling Angular2 - January 31st, 2017
- Jedi Angular 2 Tips and Tricks - March 28th, 2017
- Unit Testing Angular(2+) with JSDOM - April 4th, 2017
- More Control with Angular Flex Layout - April 11th, 2017
- Angular(2+) Model Driven Forms Are Superior - April 18th, 2017
- Dynamically Add Components in Angular - April 25th, 2017
- How to Really use NgRX - May 2nd, 2017