One of the hardest things about writing non-trivial applications is managing state. In our attempt to effectively manage state, we introduce additional complexity by trying to control application flow and the amount of code volume that comes from all of this. Redux gives us the ability to address state management and communication in astonishingly simple semantics that can be explained in just a couple minutes. The beauty of Redux is that it is not only a library but a pattern that works very will within Angular 1.x applications. In this series, we are going to learn how to integrate redux (the pattern) into Eggly from scratch and then introduce ng-redux and add in Redux DevTools at the end for fun.
Welcome to the series where we are going to learn how to integrate Redux into an existing Angular application.
One of the most challenging aspects of programming is state management. The complexity involved with managing state exponentially increases with each moving piece that we introduce. For every piece of shared, mutable state that we have in our application, there is an opportunity for unintended behavior. For instance, let us say that we have a collection that is shared between two controllers; how do we modify it from one controller with absolute certainty that it isn't going to adversely affect the other?
Redux was initially created for React, but it is in fact, a really solid programming pattern that we can easily apply to our Angular applications. The beauty of Redux is its simplicity in so much that someone could create an implementation of Redux in less than 10 minutes. This is exactly what we will do in the first couple lessons as we build out our reducers, actions and application store by hand before we introduce ng-redux to handle the rest of our abstractions.
We will also learn how to combine reducers, introduce middleware for async operations, map state directly to our actions, test reducers and even hook up Redux devtools. By the end of the series, you will have the tools in your hands to tame state management within your angular application while reducing the overall complexity thanks to the simplicity of Redux.
In this lesson, we are are going to learn how to abstract out state mutations into a reducer. In our current Eggly application, all of the state and business logic for our categories is handled within the categories model. We are going to refactor our code to entirely eliminate the need for our categories model by building out a couple of reducers and seeing how we can interact with them directly.
The key thing to notice as we build out our reducers is that a reducer itself is completely stateless because it receives current state every single time it needs to calculate and return the new state. With a clearly defined contract of inputs and outputs, this makes testing reducers really easy which is one of the huge benefits of using this approach.
In this lesson, we are going to learn how to build an application store so that we can have a single place to store our application state. Reducers are great for updating state because they are stateless, but they are very bad at preserving state, which is vital when you need to reference state in more than one place.
We will build out a simple application store that will be responsible for keeping track of our categories collection that our categories controller can consume.
In this lesson, we are going to extend our application store with a subscribe method so that we do not have to manually query our application store every time we dispatch an action.
In our categories controller, we are dispatching a GET_CATEGORIES action every time we want to update the categories collection within our application store. For this change to be propagated back into our controller, we need to call store.getState.
Depending on how many times we are calling store.dispatch, this could get very cumbersome. We will extend our application by adding a subscribe method so that state changes are automatically pushed into our controller.
Manually creating action items every time we need to dispatch an action eventually accumulates to a very tedious process. In this lesson, we will learn how to abstract out the creation of action items so that we consolidate their creation into a single place. This not only creates efficiencies in how we express code but also reduces code volume while simultaneously increasing stability.
Up to this point, we have created an effective, yet rudimentary, implementation of Redux by manually creating an application store, reducers and action creators. We have already made significant progress in simplifying state management within our application, but we have in fact only touched the surface of what Redux has to offer.
In this lesson, we are going to introduce ngRedux into our application and let it handle the rest of the heavy lifting as we work through this series. ngRedux is a project that provides angular bindings to redux to streamline integrating it into our application.
In this lesson, we are going to learn how to enable our store to work with more than one reducer in an application.
Currently, we have set up our store to work with the categories reducer exclusively, but this leaves us with a real problem, what if we want to keep track of the currently selected category within our store so that we can access that data somewhere else in the app? It is safe to say that most applications consist of many data models interacting together, and we need a way to accommodate this within the single state tree in our application.
When we initialize our app store, wouldn't it be convenient if we could take all of our reducers and combine them into one big reducer that our store knows how to work with? Well, this is exactly what the combine reducer method does in Redux. It allows us to combine reducers together and then access them as a named property on our application store.
We currently have a category reducer just hanging out here doing nothing because we have been focusing entirely on the categories reducer. We are going to combine the two reducers so that we can use the current category data within our bookmarks component.
In this lesson, we are going are going to apply everything that we have learned by building out our bookmarks component to be powered by Redux. This will help reinforce all the techniques we have covered by seeing them built out in a single, contiguous lesson.
We will walk through the steps necessary to phase out the bookmarks model with a Redux implementation that includes building and integrating the equivalent reducers and action creators to integrate into our application.
One of the biggest mental shifts that developers have to take when embracing redux is shifting from mutable operations to immutable operations. The problem with mutable operations is that they create side effects that involve anything else that is referencing the thing that is being mutated.
With immutable operations within a reducer, a new object is returned for every operation – completely bypassing the possibility that an existing reference is going to be compromised. It is then up to the consumer to decide how they want to get the new data and what to do with it.
In this lesson, we are going to flesh out the CRUD functionality of our bookmarks reducer, doing it first using mutable operations and then refactoring it to use immutable operations.
In this lesson, we are going to learn how to map our Angular component directly to our application store using the connect method on ngRedux.
In Angular, it is a common technique to bind a controller property directly to the model so that our controllers remain lightweight and operate more as a pass through mechanism than anything else.
Using the connect method, we can accomplish the same effect by not only binding properties directly to the application store, but also binding our controller to our action creators. This allows us to drastically simplify our controllers as we are able to delete a bunch of local controller methods as they become unnecessary because of our mappings.
Invariably the question that comes up when talking about Redux is how does one handle asynchronous operations in redux. For instance, how do we hand off an operation to redux that requires a remote call to the server? Where exactly does the async part get handled?
Redux has this concept of middleware that allows us to insert custom logic in the space between dispatching an action, and the moment it reaches the reducer. In this lesson, we are going to set up a few async operations and then use redux thunk middleware to make sure that everything makes it into the reducer properly.
The main key to thunk middleware is that it allows us to return a function instead of an action. This function can encapsulate the async operation and dispatches the appropriate action when the operation is completed.
One of the primary advantages of using redux is that reducers are incredibly easy to test. By nature, they have a clearly defined contract of inputs and outputs, and because they are stateless, they require very little instrumentation to test in isolation.
We will start with a few simple tests for the categories reducer and then move on to testing the CRUD functionality within the bookmarks reducer.
In this lesson, we are going to learn how to integrate Redux Devtools into our Angular application.
Redux Devtools is a live-editing time travel environment for Redux. Devtools boasts a list of awesome features but my two favorite ones are that we can inspect every state and action as it happens and we can go back in time by “cancelling” actions.
This is going to be an interesting lesson because, in order for this to work, we are going to need to make something that was written for React work in Angular. For the most part, everything will play side by side with one small trick that we will pull off at the end to force an Angular digest cycle when React manipulates the application store.
Lukas Ruebbelke is a technology enthusiast and author of AngularJS in Action for Manning Publications. His favorite thing to do is get people as excited about new technology as he is. He is the co-organizer of the Arizona Software Community user group and host of multiple developer events in the Phoenix metro area.