Angular State Management Using NgRx
A Simple Introduction to the Redux Pattern with Angular’s Most Popular State Management Library
In the modern age of front end development, it’s common to separate the concerns of your application into components. While this introduces many benefits and creates a degree of simplicity, it can also introduce complexity when several components all depend on the same data.
Without forethought it is easy to end up with data dependencies between components, so that the intended benefit of compartmentalization is nothing but wishful thinking.
Any discussion of state management would be incomplete without mentioning Redux. Redux is both a pattern and the name of the library after which the pattern takes its name. Redux is most commonly associated with React applications, and although the library can also be used in Angular, it is much more common to see apps built using NgRx, which is the most popular state management library for Angular.
Setting up a project with NgRx can be daunting, as it’s a complex topic, but I want to show you a very simple way to get started, and one that you can build on once you’ve learned the fundamentals of the redux pattern.
TLDR: Show Me the Code!
Ok, hot shot, you want the code? You got it:
A simple introduction to state management in Angular with NgRx - Maloric/angular-ngrx
Go nuts. If you want to come back later for more detailed instructions, I won’t hold it against you.
It goes without saying that you’ll need an Angular project to work on. For the purposes of this tutorial, we’ll create one using the Angular CLI, but you should be able to apply the same steps to an existing project without too much trouble.
Assuming you have NPM installed, we’ll start by creating an empty project. You don’t need to install the Angular CLI globally if you use the npx command, like so:
npx ng new angular-ngrx
Accept all of the default values and then open the
angular-ngrx folder up in your favourite IDE. Also navigate to the new
angular-ngrx folder in your command line and run the following command:
npm install — save-dev @ngrx/core @ngrx/store
The NgRx library is separated out so that you can install only the bits you need, and for now we’ll focus just on these two. The store is the one we’re really interested in, so let’s get the ball rolling.
Start your app by running the following command in your command line:
npm run start
In a few seconds the app will be up and running. To check, open up your browser at http://localhost:4200/ to see it in all its glory. There’s not much to it yet, but we’ll change that in a moment.
Getting Some Data to Play With
First, we’ll need some data. We’re going to use the excellent JSONPlaceholder service to generate 100 records of fake data. Go to https://jsonplaceholder.typicode.com/posts and save the output in your new workspace as
/src/app/data/posts.ts (you’ll need to create the data folder). Wrap the output like so:
export const POSTS = <PASTE YOUR DATA HERE>;
Out First Component(s)
Now let’s create a couple of components to make use of this data. Run the following commands in a new command line (leave the app running in your original command line).
npx ng generate component posts -s -t
npx ng generate component post-count -s -t
In these examples I generated the components with inline templates and styles. If you prefer separate HTML and CSS files, just remove the
-t switches. Once the components are generated, replace the contents of
/src/app/app.component.html with the following:
When you refresh the app in your browser, you should see the following output:
> post-count works!
> posts works!
It doesn’t look pretty, but it shows that the two new components are working. Time to wire them up to some data.
Plugging the Data In
Normally we would be fetching data via a REST API or something similar. It’s always a good idea to do all of your data access outside of your component by using a service. Not only will this allow you to re-use logic across your application, but will make your components and data access logic easier to test (a topic to be covered another time).
Let’s create a new service using the CLI:
npx ng generate service blog
Nice and easy! Enjoy it while it lasts, because from here on out it’s going to get a little more complicated.
Before we start delving into NgRx, let’s get some data on the page the old fashioned way. First, update the code in your new service to match the following:
getPosts method is a stand-in for what would normally be an HTTP request, returning an Observable of posts. If you haven’t come across Observables before, think of them as Promises that can resolve multiple times with different data. They are a complex but powerful tool provided by a library called RxJs. For now, we’re returning the data as an Observable using the
of method from RxJs.
Next, let’s pull the data into our components and display it to the end user. Update
/src/app/posts/posts.component.ts like follows:
We’ve injected the posts service and assigned the result of
service.getPosts() to a public property of the component. Notice that we suffix the property with a dollar symbol? That’s a common way to indicate that a variable is an Observable, as interacting with Observables is different to interacting with normal data.
We’ve also looped through the output of
posts$ using the
async pipe. This is a shortcut for getting the data out of the observable as a “regular” property (i.e. an array of posts).
In your browser, you should now see a list of post IDs and titles. Let’s do something similar to the post-count component. Update the Typescript like so:
The difference here is that we map the posts to a value representing the number of posts.
The First Problem to be Solved
The eagle eyed amongst you may have noticed that we now have two components calling the same method on the PostsService. Bearing in mind that this method is supposed to go and fetch data from an API somewhere, that seems pretty inefficient. It also means that there is potential for our two components to disagree about how many posts there are. If the two requests complete at different times, then a post could have been deleted or added.
We could solve that by having one component talk to the other, but this can get messy very quickly, and we want to avoid components that are interdependent on each other. It makes more sense to store that kind of state in the service that both components share.
To that end, we could roll our own state management solution using RxJs, and we wouldn’t be the first. It’s certainly a viable approach — in fact, NgRx is simply an extension of that idea using the Redux pattern. So we’re going to skip over the part where we roll our own state management solution and learn how to do it using NgRx.
What is Redux?
The Redux Pattern has several defining characteristics:
- There is a single store for all (or most) application state, rather than state being stored on each component.
- Changes to the state are described in the form of actions. Each action has a type (e.g. “Delete Product”) and optionally, a payload (e.g. the ID of the product to be deleted).
- Actions are dispatched on a shared message bus and handled by pure functions called reducers. These are essentially switch statements that look for a given action type and then return a new object representing the state after the action takes effect.
With the above in mind, let’s start by creating our first reducer. As mentioned already, the reducer’s job is to handle actions and return the updated state. Traditionally, this would be written in the form of a switch statement, but nowadays NgRx provides a more structured way to do the same job. Put the following into
Defining the shape of my state is often the first thing I do. The application state can be likened to a client side database. Normally it lives only as long as the page, and is lost when the page is reloaded. But by storing all of your application state in one place, it should be trivial to later persist this in local storage, thus allowing the user to pick up where they left off. Sometimes it makes sense to fetch data from the server again, but some state information, such as which row is selected, is specific to one user and might be worth persisting between page loads.
But that’s a concern for another time. For now, let’s consider what we have and what we need. The
BlogReducer above doesn’t actually do anything yet except assign the initial state. Later we’ll add the ability to handle actions and update the state. But first, let’s wire the reducer up to our application and modify the initial state.
The first thing we’ll need to do is modify
/src/app/app.module.ts so that we can inject the NgRx store wherever it is needed. Add the right imports and then add the following to the
Next, we’ll add our first selector. A selector is responsible for getting data from the state and transforming it into the shape that a component needs. We’ll need two selectors: one for the posts, and one for the post count. These could be added to their respective components, but it is just as easy (and more reusable) to add them to the service.
A Quick Note on the
NgRx provides methods to create efficient selectors that take advantage of memoization to store previous results that have the same inputs. However, since this is an introduction, I’ll show you a simpler, but less efficient technique. For most applications, it should be more than sufficient, and you can replace these simple selectors with memoized selectors later using
Writing Our First Selector
Let’s go back to our service. NgRx utilises observables to pipe data out of the store, so we’ll employ our service to store these observables in one place. Open up the blog service and modify it to match the following:
getPosts no longer returns any data? Redux has a unidirectional data flow, meaning that components read data from the store, then dispatch actions to update the store. This is a feature of the CQRS pattern (upon which Redux is based), which advises separation between actions that read data and write it.
Earlier we identified that calling
getPosts twice was inefficient, since it will make the same HTTP call twice. Now, by subscribing to a centralized store, we only need to call the method once, which is much more efficient. With this in mind, we need to update our components to pull data out of the observables instead of
posts$ = this.service.posts$; // posts.component.ts
postCount$ = this.service.postCount$; // post-count.component.ts
Back in your browser you should notice that all our posts disappeared. You can experiment by adding posts to
initialBlogState to see if the components are wired up correctly.
Next, we’re going to create a couple of actions and tell the reducer how to handle them. For the tutorial, we will only create two actions:
ProvideProducts. The latter represents the products being loaded from an external store such as a REST API.
NgRx has made it easy for you to create new actions (not that it was hard before, but it’s even easier now). Just put the following code into
The first value passed into
createAction simply describes the action for the purposes of logging or debugging, so feel free to choose a suitably descriptive format that works for you. A common approach, if you have a large application with multiple concerns separated into multiple modules, is to prefix the action name with the module name.
The second parameter, which is optional, describes the shape of the action’s payload. While you don’t have to provide this, it does make your life easier as you utilise the actions later.
With these actions in place, all we have to do now is dispatch them. The best place to dispatch actions is in your service — your components shouldn’t care that you use NgRx.
getPosts method of your service to match the following, making sure to import the action you just created:
If you refresh your browser, you’ll notice that nothing has changed. That’s because we removed the component’s call to
getPosts earlier. Since we only need the method to be called once, we can put it in our
AppModule constructor for now. Add a constructor in
/src/app/app.module.ts like so:
But wait, it STILL isn’t working. That’s because we haven’t told the reducer how to handle the
ProvidePosts action yet, so our state still has an empty array where our posts should be.
Handling Actions in the Reducer
/src/app/data/reducer.ts and change
BlogReducer to the following, being sure to import the
on method from NgRx and your
If you’re comfortable with some of the fancier parts of ES6 and Typescript, then feel free to use the slightly less verbose second approach.
Back in the browser, you should find the posts have reappeared, along with the correct count at the top of the page. Congratulations, you just made your first NgRx app!
We still have one action we haven’t used. If you’d like to put some of your newfound knowledge into practice, try adding a delete button to each post and dispatching the
DeletePost action whenever a it is clicked. You’ll also need to update the reducer so that it can handle the
If you get stuck, check out the link to my GitHub repo at the top of this page, and if you’re still stuck, come and find me on Twitter.
You might be wondering what the benefit is — after all, it feels like a lot of work just to display some posts. Here are just some of the benefits I can think of:
- Multiple components can use the same data without depending on each other. This leads to a simple architecture. Less complexity === less problems.
- Actions provide a point of separation that makes components easier to test.
- Shaping your data via selectors encourages re-use, which in turn increases performance. In the case of our
posts$selector, it is only run once when the data changes, even though the same data is used to inform the
- Having easy access to the application’s data makes it much easier to extend related functionality. It is often simply a case of writing a new selector for existing data.
- A single state is incredibly useful when debugging your application. The Redux DevTools Chrome extension is especially useful.
- The hardest part of any redux pattern is the initial setup. As the application grows, it pays for itself time and time again.
- The advanced features of NgRx provide further performance benefits, such as memoization.
Redux isn’t a one size fits all approach, but it is a flexible one. The approach we’ve taken here is a shallow dip into NgRx, but there are advanced capabilities such as side effects, feature state, advanced selectors and more. For smaller applications you might not even need redux at all, but you may grow to like it enough that you won’t consider building an app without it.