Brian Love
Google Developer Expert in Angular, software engineer and skier located in Denver, CO

Angular + NgRx: The Basics

Reading time ~8 minutes

Learn the basics of implementing the Redux pattern in Angular applications.

Before we dive into implementing NgRx in an Angular application we should first stop and consider if the Redux pattern is nexessary for our app. Every app is different, so your-mileage-may-vary, and these are generally my opinions and thoughts on implementing the Redux pattern using NgRx in Angular.

Series

This post is part of a series on using NgRX, the Redux-inspired, predictable state container for JavaScript:

  1. NgRX: The Basics (this post)
  2. NgRX: Getting Started
  3. NgRX: Refactor Module

Example Project

You can download the source code for the completed NgRx application at:

What is Redux?

Redux is a predictable state container for JavaScript.

Predictable

Redux is predictable because Redux imposes limitations upon how we can change or mutate the state of our application and the domain data that our application uses. We’ll dive more into these limitations shortly. Due to these limitations and the immutabilty of the objects our application uses, the data our application uses is reliable and predictable.

State Container

At the core of Redux is the state container, which we call the store. The store is simply an object that hold’s our application’s state. It’s a tree structure, think directories, that contains all of the entities that our application uses. Some of these entities might include things like: users, orders, and line items. In Redux we only have a single store that contains the state for the entire application.

Pros

There is some debate as to whether the Redux pattern is essential for a Single Page Application. I would say “no”, using Redux is not necessary for every and all projects.

However, using the Redux pattern provides some real benefits:

  1. Consist behavior.
  2. Can run your application in multiple environments (client, server, native).
  3. Easy to test and to debug.
  4. A great developer experience.

It is also important to note that redux is framework agnostic. It’s a pattern, not a specific implementation that requires the use of a specific framework, such as React or Angular.

Historically speaking, Redux was inspired by the Flux pattern. Flux is an application architecture developed by Facebook for building user interfaces, and is implemented in React.

We’ll be focusing on the NgRx implementation of the Redux pattern in Angular.

Cons

If you are considering using the Redux pattern, and perhaps more specifically, NgRx in your Angular application, you should consider if it is necessary. It might not be necessary because there are some drawbacks:

  1. Adds complexity.
  2. Imposes limitations.
  3. Requires understanding Redux core concepts.

When you begin implementing the Redux pattern using NgRx with Angular, you’re stepping a bit beyond the boundaries of being just an “Angular developer”. This might be challenging for some, but it can also be a lot of fun and a great experience.

Let’s dive into the core concepts of the Redux pattern, and show some examples using NgRx.

Core Concepts

We need to understand a few core concepts before we can start using NgRx:

  1. Actions
  2. Reducers
  3. Effects

Actions

An action is an object that represents an intent to mutate or change the state of our application. An action could be:

  1. Loading data from a REST API.
  2. Opening a sidebar or showing a dialog.
  3. Routing to a different view.

An action is an object that contains at least one property: type. The type should be a string, which makes our actions serializable.

Here’s what a simple action object might look like:

{
  type: 'LOAD_USER'
}

We might also specify a payload object to be dispatched along with our action:

{
  type: 'LOAD_USER',
  payload: { id: 1 }
}

In the example above I am intending to load a user, likely via an HTTP request, and I am specifying the user’s id as part of the payload object.

Here’s a better example of an action object that implements the Action interface provided by NgRx:

export class LoadUser implements Action {
  readonly type = 'LOAD_USER';

  constructor(public payload: { id: number }) { }
}

We’ll be using the dispatch() method on the Store object to notify the store that we want to take a specific action.

Reducers

The reducer in the Redux pattern has the responsibility of acting upon an action to mutate the state of our application. We only mutate the state of our application within a reducer() function.

While it might be tempting as a developer to mutate an object that we retreive from the store outside of a reducer function, we’ll prevent any tampering with objects by using the ngrx-store-freeze package. If we attempt to mutate an object we’ll get an exception when we run our application. We should only use the ngrx-store-freeze meta reducer when running our application in a development environment.

A reducer is a function. In fact, a reducer is a pure function. According to wikipedia, a pure function must meet two criteria:

First:

The function always evaluates the same result value given the same argument value(s). The function result value cannot depend on any hidden information or state that may change while program execution proceeds or between different executions of the program, nor can it depend on any external input from I/O devices.

Second:

Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices

Here’s an example of a reducer function:

function reducer(state, action) {
  switch (action.type) {
    case 'LOAD_USER':
      return { ...state, loading: true };
    case 'LOAD_USER_SUCCESS'
      return { ...state, loading: false, user: action.payload }
    default:
      return state;
  }
}

A few things to note about the reducer() function above:

  • First, our reducer() function accepts two arguments: state and action. The state object is the current state of the application. The action object is the action that was dispatched by our application that intends to mutate the state of our application.
  • Next, we are going to switch on the action’s type value. Remember, this is just a string constant that we defined when we declared the action.
  • Note that our reducer() function will mutate the state of the application and return this new state object. We do this by using the spread operator (the leading three dots before the object), which returns a copy of our state object. We then override the loading and the user properties stored in the state object.
  • When the LOAD_USER action is dispatched, we are changing the loading state of our application, setting the value to true.
  • When the LOAD_USER_SUCCESS action is dispatched, we are changing the loading state to false, and we are also storing the user, which is the payload object for our action.
  • Finally, note that we return the current state object by default. Don’t forget to do this, or you’ll see some odd behavior in your application.

Effects

So far we can define action objects that intend to mutate the state of our application, and we can create reducer() pure functions that will mutate the state of our application. But, what about actually performing the HTTP request to load our user. This is where effects come into play.

An effect listens for actions that are dispatched, and performs some side effect, usuaully an asynchronous request. The benefit to using effects is that it enables us to isolate our async requests outside of our components, making our components less complex. Now, our components just dispatch actions and listen for values to be emitted from the store when our state changes.

Let’s look at an effect that loads a user:

class UsersEffects {

  @Effect()
  loadUser: Observable<Action> = this.actions.ofType<LoadUser>('LOAD_USER')
    .pipe(
      map(action => action.payload),
      switchMap(payload => this.userService.getUser(payload.id)),
      map(user => new LoadUserSuccess(user))
      catchError(error => new LoadUserError(error))
    )
}

The code above is specific to the NgRx implementation of the Redux pattern. The example also makes use of the pipe() method that is implemented on an Observable when using RxJS 5.5.x, as well as several operators (functions) that are provided by RxJS.

The important things to understand are:

  • We have defined a UsersEffects class, which has a single loadUser property.
  • We are filtering our actions so that we only perform this particular side effect when the LOAD_USER action is dispatched.
  • We are going to use the payload property on our action to invoke the getUser() method that we have defined in the UserService.
  • We then dispatch a new LoadUserSuccess action, specifying the retrieved user object as the payload for the new action.
  • Finally, we catch any errors and displatch a new LoadUserError action, specifying the error object as the payload.

In summary, an effect enables us to perform asynchronous side effects that will dispatch new actions as necessary.

RxJs

You may have noticed the pipe() operator along with several other operators (or functions) in the previous example for creating an effect to load a user. This is not part of NgRx, rather, these operators are provided by the RxJS library.

According to the RxJS website:

RxJS is a library for reactive programming using Observables, to make it easier to compose asynchronous or callback-based code.

RxJS and NgRx play together very well. And, RxJs plays well with Angular. In fact, RxJs is a dependency in Angular. So, if you’re using Angular, then you’re already using RxJs.

If you are unfamiliar with RxJS, I suggest you check out the tutorial provided by RxJS.

Conclusion

In conclusion, the Redux pattern provides several benefits when building Single Page Applications with Angular. And, NgRx is the defacto standard for using the Redux pattern in Angular.

While the Redux pattern, and NgRx in turn, may not be the best solution for your project, it provides several benefits for building Angular applications.

In the next post in this series we’ll implement the Redux pattern using NgRx into an existing project, a Tour of Heroes application.

Brian Love

Hi, I'm Brian. I am interested in TypeScript, Angular and Node.js. I'm married to my best friend Bonnie, I live in Denver and I ski (a lot).