Tutorial: Building Redux in TypeScript with Angular 2

July 29, 2016 0 Comments redux, angular 2, typescript

Tutorial: Building Redux in TypeScript with Angular 2

tl;dr - In this post we'll be looking at a data-architecture pattern called Redux. In this post we're going to discuss:

  • the ideas behind Redux,

  • build our own mini version of the Redux Store and
  • hook it up to Angular 2.

You can get the completed code here
You can try the demo here
Discuss on HN here

For many Angular projects we can manage state in a fairly direct way: We tend to grab data from services and render them in components, passing values down the component tree along the way.

Managing our apps in this way works fine for smaller apps, but as our apps grow, having multiple components manage different parts of the state becomes cumbersome. For instance, passing all of our values down our component tree suffers from the following downsides:

Intermediate property passing - In order to get state to any component we have to pass the values down through inputs. This means we have many intermediate components passing state that it isn't directly using or concerned about

Inflexible refactoring - Because we're passing inputs down through the component tree, we're introducing a coupling between parent and child components that often isn't necessary. This makes it more difficult to put a child component somewhere else in the hierarchy because we have to change all of the new parents to pass the state

State tree and DOM tree don't match - The "shape" of our state often doesn't match the "shape" of our view/component hierarchy. By passing all data through the component tree via props we run into difficulties when we need to reference data in a far branch of the tree

State throughout our app - If we manage state via components, it's difficult to get a snapshot of the total state of our app. This can make it hard to know which component "owns" a particular bit of data, and which components are concerned about changes

Pulling data out of our components and into services helps a lot. At least if services are the "owners" of our data, we have a better idea of where to put things. But this opens a new question: what are the best practices for "service-owned" data? Are there any patterns we can follow? In fact, there are.

In this post, we're going to discuss a data-architecture pattern called Redux which was designed to help with these issues. We'll implement our own version of Redux which will store all of our state in a single place. This idea of holding all of our application's state in one place might sound a little crazy, but the results are surprisingly delightful.


If you haven't heard of Redux yet you can read a bit about it on the official website. Web application data architecture is evolving and the traditional ways of structuring data aren't quite adequate for large web apps. Redux has been extremely popular because it's both powerful and easy to understand.

Data architecture can be a complex topic and so Redux's best feature is probably its simplicity. If you strip Redux down to the essential core, Redux is fewer than 100 lines of code.

We can build rich, easy to understand, web apps by using Redux as the backbone of our application. But first, let's walk through how to write a minimal Redux and later we'll work out patterns that emerge as we work out these ideas in a larger app.

There are several attempts to use Redux or create a Redux-inspired system that works with Angular. Two notable examples are:

ngrx is a Redux-inspired architecture that is heavily observables-based. angular2-redux uses Redux itself as a dependency, and adds some Angular helpers (dependency-injection, observable wrappers).
Here we're not going to use either. Instead, we're going to use Redux directly in order to show the concepts without introducing a new dependency. That said, both of these libraries may be helpful to you when writing your apps.

Redux: Key Ideas

The key ideas of Redux are this:

  • All of your application's data is in a single data structure called the state which is held in the store
  • Your app reads the state from this store
  • This store is never mutated directly
  • User interaction (and other code) fires actions which describe what happened
  • A new state is created by combining he old state and the action by a function called the reducer.

If the above bullet list isn't clear yet, don't worry about it - putting these ideas into practice is the goal of the rest of this post.

Table of Contents

Core Redux Ideas

What's a reducer?

Let's talk about the reducer first. Here's the idea of a reducer: it takes the old state and an action and returns a new state.

A reducer must be a pure function. That is:

  1. It must not mutate the current state directly
  2. It must not use any data outside of its arguments

Put another way, a pure function will always return the same value, given the same set of arguments. And a pure function won't call any functions which have an effect on the outside world, e.g. no database calls, no HTTP calls, and no mutating outside data structures.

Reducers should always treat the current state as read-only. A reducer does not change the state instead, it returns a new state. (Often this new state will start with a copy of old state, but let's not get ahead of ourselves.)

Let's define our very first reducer. Remember, there are three things involved:

  1. An Action, which defines what to do (with optional arguments)
  2. The state, which stores all of the data in our application
  3. The Reducer which takes the state and the Action and returns a new state.

Defining Action and Reducer Interfaces

Since we're using TypeScript we want to make sure this whole process is typed, so let's setup an interface for our Action and our Reducer:

The Action Interface

Our Action interface looks like this:

Notice that our Action has two fields:

The type will be an identifying string that describes the action like INCREMENT or ADD_USER. The payload can be an object of any kind. The ? on payload? means that this field is optional.

The Reducer Interface

Our Reducer interface looks like this:

Our Reducer is using a feature of TypeScript called generics. In this case type T is the type of the state. Notice that we're saying that a valid Reducer has a function which takes a state (of type T) and an action and returns a new state (also of type T).

Creating Our First Reducer

The simplest possible reducer returns the state itself. (You might call this the identity reducer because it applies the identity function on the state. This is the default case for all reducers, as we will soon see).

We're not using the Action yet, but let's try this Reducer just the same.

Running Our First Reducer

Let's put it all together and run this reducer:

And run it:

It seems almost silly to have that as a code example, but it teaches us our first principle of reducers:

By default, reducers return the original state.

But let's do something more interesting and make our state change.

Adjusting the Counter With actions

Eventually our state is going to be much more sophisticated than a single number. We're going to be holding the all of the data for our app in the state, so we'll need better data structure for the state eventually.

That said, using a single number for the state lets us focus on other issues for now. So let's continue with the idea that our state is simply a single number that is storing a counter.

Let's say we want to be able to change the state number. Remember that in Redux we do not modify the state. Instead, we create actions which instruct the reducer on how to generate a new state.

Let's create an Action to change our counter. Remember that the only required property is a type. We might define our first action like this:

We should also create a second action that instructs our reducer to make the counter smaller with:

Now that we have these actions, let's try using them in our reducer:

And now we can try out the whole reducer:

Neat! Now the new value of the state is returned according to which action we pass into the reducer.

Reducer switch

Instead of having so many if statements, the common practice is to convert the reducer body to a switch statement:

Q: Wait, all of my application state is in one giant switch statement?
A: Yes and no.
If this is your first exposure to Redux reducers it might feel a little weird to have all of your application state changes be the result of a giant switch. There are two things you should know:

  1. Having your state changes centralized in one place can help a ton in maintaining your program, particularly because it's easy to track down where the changes are happening when they're all together. (Furthermore, you can easily locate what state changes as the result of any action because you can search your code for the token specified for that action's type)
  2. You can (and often do) break your reducers down into several sub-reducers which each manage a different branch of the state tree. We'll talk about this later.

Action "Arguments"

In the last example our actions contained only a type which told our reducer either to increment or decrement the state.

But often changes in our app can't be described by a single value - instead we need parameters to describe the change. This is why we have the payload field in our Action.

In this counter example, say we wanted to add 9 to the counter. One way to do this would be to send 9 INCREMENT actions, but that wouldn't be very efficient, especially if we wanted to add, say, 9000.

Instead, let's add a PLUS action that will use the payload parameter to send a number which specifies how much we want to add to the counter. Defining this action is easy enough:

Next, to support this action, we add a new case to our reducer that will handle a 'PLUS' action:

In the first line we take the state 3 and PLUS a payload of 7, which results in 10. Neat! However, notice that while we're passing in a state, it doesn't really ever change. That is, we're not storing the result of our reducer's changes and reusing it for future actions.

Storing Our State

Our reducers are pure functions, and do not change the world around them. The problem is, in our app, things do change. Specifically, our state changes and we need to keep the new state somewhere.

In Redux, we keep our state in the store. The store has the responsibility of running the reducer and then keeping the new state. Let's take a look at a minimal store:

Notice that our Store is generically typed - we specify the type of the state with generic type T. We store the state in the private variable _state.

We also give our Store a Reducer, which is also typed to operate on T, the state type this is because each store is tied to a specific reducer. We store the Reducer in the private variable reducer.

In Redux, we generally have 1 store and 1 top-level reducer per application.

Let's take a closer look at each method of our State:

  • In our constructor we set the _state to the initial state.
  • getState() simply returns the current _state
  • dispatch takes an action, sends it to the reducer and then updates the value of _state with the return value

Notice that dispatch doesn't return anything. It's only updating the store's state (once the result returns). This is an important principle of Redux: dispatching actions is a "fire-and-forget" maneuver. Dispatching actions is not a direct manipulation of the state, and it doesn't return the new state.

When we dispatch actions, we're sending off a notification of what happened. If we want to know what the current state of the system is, we have to check the state of the store.

Using the Store

Let's try using our store:

We start by creating a new Store and we save this in store, which we can use to get the current state and dispatch actions.

Being Notified with subscribe

It's great that our Store keeps track of what changed, but in the above example we have to ask for the state changes with store.getState(). It would be nice for us to know immediately when a new action was dispatched so that we could respond. To do this we can implement the Observer pattern - that is, we'll register a callback function that will subscribe to all changes.

Here's how we want it to work:

  1. We will register a listener function using subscribe
  2. When dispatch is called, we will iterate over all listeners and call them, which is the notification that the state has changed.

Registering Listeners

Our listener callbacks are a going to be a function that takes no arguments. Let's define an interface that makes it easy to describe this:

After we subscribe a listener, we might want to unsubscribe as well, so lets define the interface for an unsubscribe function as well:

Not much going on here - it's another function that takes no arguments and has no return value. But by defining these types it makes our code clearer to read.

Our store is going to keep a list of ListenerCallbacks let's add that to our Store:

Now we want to be able to add to that list of _listeners with a subscribe function:

Notifying Our Listeners

Whenever our state changes, we want to call these listener functions. What this means is, whenever we dispatch a new action, whenever the state changes, we want to call all of the listeners:

The Complete Store

We'll try this out below, but before we do that, here's the complete code listing for our new Store:

Trying Out subscribe

Now that we can subscribe to changes in our store, let's try it out:

Above we subscribe to our store and in the callback function we'll log subscribed: and then the current store state.

Notice that the listener function is not given the current state as an argument. This might seem like an odd choice, but because there are some nuances to deal with, it's easier to think of the notification of state changed as separate from the current state. Without digging too much into the weeds, you can read more about this choice here, here, and here.

We store the unsubscribe callback and then notice that after we call unsubscribe() our log message isn't called. We can still dispatch actions, we just won't see the results until we ask the store for them.

If you're the type of person who likes RxJS and Observables, you might notice that implementing our own subscription listeners could also be implemented using RxJS. You could rewrite our Store to use Observables instead of our own subscriptions.
In fact, we've already done this for you and you can find the sample code in the file code/redux/angular2-redux-chat/minimal/tutorial/06b-rx-store.ts.
Using RxJS for the Store is an interesting and powerful pattern if you're willing to us RxJS for the backbone of our application data.
Here we're not going to use Observables very heavily, particularly because we want to discuss Redux itself and how to think about data architecture with a single state tree. Redux itself is powerful enough to use in our applications without Observables.
Once you get the concepts of using "straight" Redux, adding in Observables isn't difficult (if you already understand RxJS, that is). For now, we're going to use "straight" Redux and we'll give you some guidance on some Observable-based Redux-wrappers at the end.

The Core of Redux

The above store is the essential core of Redux. Our reducer takes the current state and action and returns a new state, which is held by the store.

Here's our minimal TypeScript Redux stores in one image (click for larger version):

There are obviously many more things that we need to add to build a large, production web app. However, all of the new ideas that we'll cover are patterns that flow from building on this simple idea of an immutable, central store of state. If you understand the ideas presented above, you would be likely to invent many of the patterns (and libraries) you find in more advanced Redux apps.

There's still a lot for us to cover about day-to-day use of redux though. For instance, we need to know:

  • How to carefully handle more complex data structures in our state
  • How to be notified when our state changes without having to poll the state (with subscriptions)
  • How to intercept our dispatch for debugging (a.k.a. middleware)
  • How to compute derived values (with selectors)
  • How to split up large reducers into more manageable, smaller ones (and recombine them)
  • How to deal with asynchronous data

While we'll explain several of these in this post, if you want to go more in-depth with a Redux example with Angular two, checkout the Intermediate Redux chapter in ng-book 2

Let's first deal with handling more complex data structures in our state. To do that, we're going to need an example that's more interesting than a counter. Let's start building a chat app where users can send each other messages.

A Messaging App

In our messaging app, as in all Redux apps, there are three main parts to the data model:

Messaging App state

The state in our counter app was a single number. However in our messaging app, the state is going to be an object.

This state object will have a single property, messages. messages will be an array of strings, with each string representing an individual message in the application. For example:

We can define the type for the app's state like this:

Messaging App actions

Our app will process two actions: ADD_MESSAGE and DELETE_MESSAGE.

The DELETE_MESSAGE action object will delete a specified message from the state. A challenge here is that we have to be able to specify which message we want to delete.

If our messages were objects, we could assign each message an id property when it is created. However, to simplify this example, our messages are just simple strings, so we'll have to get a handle to the message another way. The easiest way for now is to just use the index of the message in the array (as a proxy for the ID).

With that in mind, the DELETE_MESSAGE action object has this shape:

We can define the types for these actions by using the interface ... extends syntax in TypeScript:

Messaging App reducer

Remember that our reducer needs to handle two actions: ADD_MESSAGE and DELETE_MESSAGE. Let's talk about these individually.


We start by switching on the action.type and handling the ADD_MESSAGE case.

TypeScript objects already have a type, so why are we adding a type field?
There are many different ways we might choose to handle this sort of "polymorphic dispatch". Keeping a string in a type field (where type means "action-type") is a straightforward, portable way we can use to distinguish different types of actions and handle them in one reducer. In part, it means that you don't have to create a new interface for every action.
That said, it would be more satisfying to be able to use reflection to switch on the concrete type. While this might become possible with more advanced type guards, this isn't currently possible in today's TypeScript.
Broadly speaking, types are a compile-time construct and this code is compiled down to JavaScript and we can lose some of the typing metadata.
That said, if switching on a type field bothers you and you'd like to use language features directly, you could use the decoration reflection metadata. For now, a simple type field will suffice.

Adding an Item Without Mutation

When we handle an ADD_MESSAGE action, we need to add the given message to the state. As will all reducer handlers, we need to return a new state. Remember that our reducers must be pure and not mutate the old state.

What would be the problem with the following code?

The problem is that this code mutates the state.messages array, which changes our old state! Instead what we want to do is create a copy of the state.messages array and add our new message to the copy.

The syntax <AddMessageAction>action will cast our action to the more specific type. That is, notice that our reducer takes the more general type Action, which does not have the message field. If we leave off the cast, then the compiler will complain that Action does not have a field message.
Instead, we know that we have an ADD_MESSAGE action so we cast it to an AddMessageAction. We use parenthesis to make sure the compiler knows that we want to cast action and not action.message.

Remember that the reducer must return a new AppState. When we return an object from our reducer it must match the format of the AppState that was input. In this case we only have to keep the key messages, but in more complicated states we have more fields to worry about.

Deleting an Item Without Mutation

Remember that when we handle the DELETE_MESSAGE action we are passing the index of the item in the array as the faux ID. (Another common way of handling the same idea would be to pass a real item ID.) Again, because we do not want to mutate the old messages array, we need to handle this case with care:

Here we use the slice operator twice. First we take all of the items up until the item we are removing. And we concatenate the items that come after.

There are four common non-mutating operations:

  • Adding an item to an array
  • Removing an item from an array
  • Adding / changing a key in an object
  • Removing a key from an object

The first two (array) operations we just covered. We'll talk more about the object operations further down, but for now know that a common way to do this is to use Object.assign. As in:

You can think of Object.assign as merging objects in from the right into the object on the left. newObject is merged into oldObject which is merged into {}. This way all of the fields in oldObject will be kept, except for where the field exists in newObject. Neither oldObject nor newObject will be mutated.
Of course, handling all of this on your own takes great care and it is easy to make a mistake. This is one of the reasons many people use Immutable.js, which is a set of data structures that help enforce immutability.

Trying Out Our Actions

Now let's try running our actions:

Here we start with a new store and we call store.getState() and see that we have an empty messages array.

Next we add three messages to our store. For each message we specify the type as ADD_MESSAGE and we cast each object to an AddMessageAction.

Finally we log the new state and we can see that messages contains all three messages.

Our three dispatch statements are a bit ugly for two reasons:

  1. we manually have to specify the type string each time. We could use a constant, but it would be nice if we didn't have to do this and
  2. we're manually casting to an AddMessageAction

Instead of creating these objects as an object directly we should create a function that will create these objects. This idea of writing a function to create actions is so common in Redux that the pattern has a name: Action Creators.

Action Creators

Instead of creating the ADD_MESSAGE actions directly as objects, let's create a function to do this for us:

You definitely don't have to use static methods for your action creators. You could use plain functions, functions in a namespace, even instance methods on an object, etc. The key idea is to keep them organized in a way that makes them easy to use.

Now let's use our new action creators:

This feels much nicer!

Using Real Redux

Now that we've built our own mini-redux you might be asking, "What do I need to do to use the real Redux?" Thankfully, not very much. Let's update our code to use the real Redux now!

If you haven't already, you'll want to run npm install in the code/redux/angular2-redux-chat/minimal/tutorial directory.

Next, instead of specifying our initial state when we create the store instead we're going to let the reducer create the initial state. Here we'll do this as the default argument to the reducer. This way if there is no state passed in (e.g. the first time it is called at initialization) we will use the initial state:

What's neat about this is that the rest of our reducer stays the same!

The last thing we need to do is create the store using the createStore helper method from Redux:

After that, everything else just works!

Now that we have a handle on using Redux in isolation, the next step is to hook it up to our web app. Let's do that now.

Using Redux in Angular

In the last section we walked through the core of Redux and showed how to create reducers and use stores to manage our data in isolation. Now it's time to level-up and integrate Redux with our Angular components.

In this section we're going to create a minimal Angular app that contains just a counter which we can increment and decrement with a button.

By using such a small app we can focus on the integration points between Redux and Angular and then we can move on to a larger app in the next section. But first, let's see how to build this counter app!

Here we are going to be integrating Redux directly with Angular without any helper libraries in-between. There are several open-source libraries with the goal of making this process easier, and you can find them in the references section below.
That said, it can be much easier to use those libraries once you understand what is going on underneath the hood, which is what we work through here.

Planning Our App

If you recall, the three steps to planning our Redux apps are to:

  1. Define the structure of our central app state
  2. Define actions that will change that state and
  3. Define a reducer that takes the old state and an action and returns a new state.

For this app, we're just going to increment and decrement a counter. We did this in the last section, and so our actions, store, and reducer will all be very familiar.

The other thing we need to do when writing Angular apps is decide where we will create components. In this app, we'll have a top-level CounterApp which will have one component, the CounterComponent which contains the view we see in the screenshot.

At a high level we're going to do the following:

  1. Create our Store and make it accessible to our whole app via dependency injection
  2. Subscribe to changes to the Store and display them in our components
  3. When something changes (a button is pressed) we will dispatch an action to the Store.

Enough planning, let's look at how this works in practice!

Setting Up Redux

We start by importing a few things we'll need along the way:

Defining the Application State

Let's take a look at our AppState:

Defining the Reducers

Next lets define the reducer which will handle incrementing and decrementing the counter in the application state:

The counterReducer handles two actions: INCREMENT, which adds 1 to the current counter and DECREMENT, which subtracts 1. Both actions use Object.assign to ensure that we don't mutate the old state, but instead create a new object that gets returned as the new state.

Since we're here, let's look at the action creators

Defining Action Creators

Creating the Store

Now that we have our reducer and state, we could create our store like so:

However, one of the awesome things about Redux is that it has a robust set of developer tools. Specifically, there is a Chrome extension that will let us monitor the state of our application and dispatch actions.

What's really neat about the Redux Devtools is that it gives us clear insight to every action that flows through the system and it's affect on the state.

Go ahead and install the Redux Devtools Chrome extension now!

In order to use the Devtools we have to do one thing: add it to our store.

Not everyone who uses our app will necessarily have the Redux Devtools installed. The code above will check for window.devToolsExtension, which is defined by Redux Devtools, and if it exists, we will use it. If it doesn't exist, we're just returning an identity function ( f => f) that will return whatever is passed to it.

Middleware is a term for a function that enhances the functionality of another library. The Redux Devtools is one of many possible middleware libraries for Redux. Redux supports lots of interesting middleware and it's easy to write our own.
You can read more about Redux middleware here

In order to use this devtools we pass it as middleware to our Redux store:

Now whenever we dispatch an action and change our state, we can inspect it in our browser!

CounterApp Component

Now that we have the Redux core setup, let's turn our attention to our Angular components. Let's create our top-level app component, CounterApp. This will be the component we use to bootstrap Angular:

All this component does is create an instance of the CounterComponent, which we'll define in a minute. But before we define CounterComponent, let's bootstrap our app.

Providing the Store

We're going to use the CounterApp as the root component, so we could bootstrap like this:

But remember that since this is a Redux app, we need to make our store instance accessible everywhere in our app. How should we do this? We'll use dependency injection (DI).

When we want to make something available via DI, then we use the provide function from @angular/core.

When we provide something to the DI system, we specify two things:

  1. the token to use to refer this injectable dependency
  2. the way to inject the dependency

Oftentimes if we want to provide a singleton service we might use the useClass option as in:

In the case above, we're using the class SpotifyService as the token in the DI system. The useClass option tells Angular to create an instance of SpotifyService and reuse that instance whenever the SpotifyService injection is requested (e.g. maintain a Singleton).

One problem with us using this method is that we don't want Angular to create our store - we did it ourselves above with createStore. We just want to use the store we've already created.

The one thing we have left to figure out is what token we want to use to inject. Our store is of type Store<AppState>:

Store is an interface, not a class and, unfortunately, we can't use interfaces as a dependency injection key.

If you're interested in why we can't use an interface as a DI key, it's because TypeScript interfaces are removed after compilation and not available at runtime.
If you'd like to read more, see here, here, and here.

This means we need to create our own token that we'll use for injecting the store. Thankfully, Angular makes this easy to do. Let's create this token in it's own file so that way we can import it from anywhere in our application;

Now we can use this token AppStore in provide. Let's do that now.

bootstrapping the App

Back in app.ts, let's bootstrap our app:

Now we are able to get a reference to our Redux store anywhere in our app by injecting AppStore. The place we need it most now is our CounterComponent.

The CounterComponent

With our setup out of the way, we can start creating our component that actually displays the counter to the user and provides buttons for the user to change the state.

Let's start by looking at the imports:

We import Store from Redux as well as our injector token AppStore, which will get us a reference to the singleton instance of our store. We also import the AppState type, which helps us know the structure of the central state.

The template

Let's look at the template of our CounterComponent:

The three things to note here are that we're:

  1. displaying the value of the counter in {{ counter }}
  2. calling the increment() function in a button and
  3. calling the decrement() function in a button.

The constructor

Remember that we need this component depends on the Store, so we need to inject it in the constructor. This is how we use our custom AppStore token to inject a dependency:

The store will call subscribe only when a new action is dispatched, so in this case we need to make sure we manually call readState at least once to ensure that our component gets the initial data.

We define two helper methods: increment and decrement, each of which dispatch their respective actions to the store.

Putting It All Together

Here's the full listing of our CounterComponent

Try it out!

Congratulations! You've created your first Angular and Redux app!

What's Next

Now that we've built a basic app using Redux and Angular, we should try building a more complicated app. When we build bigger apps we encounter new challenges like:

  • How do we combine reducers?
  • How do we extract data from different branches of the state?
  • How should we organize our Redux code?

The next step is to build an intermediate Redux chat app. The code is open-source here and we go over how to build it step-by-step in the ng-book 2 Intermediate Redux Chapter. If you found this book helpful, go grab a copy - you'll become an Angular expert in no time.


If you want to learn more about Redux, here are some good resources:

To learn more about Redux and Angular checkout:

Tutorial: Building Redux in TypeScript with Angular 2

Tag cloud