Deep dive into ngrx – video

Join us for a deep dive into ngrx where we will cover how to implement a store, write reducers, create actions, and implement effects. We will show you the ins and outs of how to implement redux in your application. This was filmed at our March 2017 Rocky Mountain Angular meetup.

(transcription)
I want to begin with stating that you might not need Redux. I think it’s important to understand that it may not be the best fit for now, usually when these things become popular everybody sort of rushes into something thinking that it’s going to solve all of their problems. There’s a good article i linked to here from Dan Abramov if you’re familiar with react and router and react community, he’s done a lot of work; created the redux pattern. But he’s got a good article that kind of covers some of the reasons you might not chose to use it. For one, for a small app it’s probably overkill.

A lot of people get into all of the pieces and wonder why you have to change three files to add a new feature or, do something that deals with all this boilerplate and things like that, which may not benefit a tiny app. However, as your application starts to grow, and you’re dealing with more complexity, it becomes something that can help scale your app. For many small apps, they’ll scale just fine without Redux. There may be a bit of a learning curve for developers who aren’t as comfortable with functional programming paradigms. I would argue these programmers should be comfortable with the functional paradigm it’s very much a part of the language.

We’ve seen a re-emergence of functional programing within the past few years, and a lot of that is been driven by React and Angular with the coming and Angular2 observables, things like that. Depending on your team’s background, there may be some overhead in getting people up to speed. It also poses some constraints, so the application needs to be represented using plain objects and arrays so that its serializable, and we’ll talk a little more about what this means and what these constraints are all about. But it does impose by implementing this architecture we take on the liability of dealing with these constraints. In the case that we don’t, you begin to lose the benefits, and then you’ve added a layer of indirection that doesn’t really give you anything.

So we’ll see that changes in the system are also described using plain objects and all of the logic for handling changes needs to be implemented as pure functions as we’ll start to see when we talk about reducers. With that in mind, a simple app might look something like this. We tend to have some views and some data that we’re providing to those views. But as an application grows, we tend to end up with copies of that data. As we have more and more components, this data becomes harder and harder to keep track of. As the user is interacting with an application, it becomes difficult to keep track of how that data is changing and flowing through the app. In other words, as our app grows, it tends to naturally become more complex.

We have different ways of dealing with this, for example, in the case of having many components that are not related in a parent-child relationship, we may need to communicate between those components and share data. We can do this in Angular with services, or reventing, and Angular apps if you’ve ever used broadcast events, route scope, to keep components de-coupled. You’ve probably run into this case where it becomes very hard to know who’s emitting those events, who’s subscribing to them, who’s changing what data you throw into a data binding and at some point it just becomes a spaghetti mass. This was a problem that Facebook ran into, and they built an architecture to deal with this problem, and this is what they called Flux. Its an architecture, it’s not an implementation of all of their libraries for implementing Flux both for React, Angular One, and Angular2. Redux is basically a simplified version. It’s basically Flux Light with functional programming applied to it, and we’ll see what that looks like.

The major benefit of Flux introduce is basically transactional state management. Instead of dealing with applications state all over the place, we actually isolate how things change, from how they look. This has a number of benefits. One we have deterministic state, meaning that we always know what our state is. If we imagine our UI as a function that takes as an argument, an object, an application state. If it is a pure function, it will always return the same thing. If we imagine our UI as a function that returns a we can always give it a state object, and given that input, have the same view every single time. That predictability gives us a number of benefits in terms of testability, reproducing the same view, and not dealing with unpredictability. I mentioned that the state is a plain job object, meaning that its serializable. We can take that state and persist it and at a later time, boot the app from it. If it’s deterministic, we’ll always have the same thing given some initial state. Again, if we can serialize some initial state snapshot, as well as a series of user actions, which are also plain objects. We can take that snapshot, replay those actions, and reproduce a scenario. For example, if a user is interacting with your app, and some error occurs, we get to take that state snapshot, all of the actions that led up to the error, serialize things, send them off so that a developer can later take that and, replay that scenario.

We have a series of commits, we can go back in time and look at some previous change. We can take that change and apply it and change and reproduce that state at any given point in time. This also facilitates being able to maintain and undo history, that’s been termed “Time Travel debugging”. I want to show quickly, the NGRX example app, so we can see what this looks like. This is available in the NGRX repository. Its meant to be a showcase of all of the pieces that make up that platform. On the left, this is the application running on the browser. On the right, i’m running a chrome extension which is the Redux in tab tools extension. What it’s showing here on the left is all of the actions that have occurred thus far in the app. I can do things here, and you’ll see that as i’m interacting, I’m actually dispatching a number of actions. I can select on something and see that there’s a new action on the list. Down here, you can see i have a new timeline so i can actually step back through all of those actions. All i’m doing is taking some state, and passing it through my component hierarchy and regenerating the view. I can go forward and back and basically replay all those actions. On this right panel, we’re seeing the diff in the state. We’ll come back to this when we start talking about some of the pieces. I can actually look at the full state object and see what i have at this particular point in time.

As I step through and see those changes. Again this is the object that can be serialized and download this thing. I can download this thing and export it and save it to a file and ship that off if I wanted to. At some later point reproduce this particular view. In summary, this is what we’re trying to get to. Being able to do these kinds of things is why you would chose to consider using redux. As our user work flows become complex, or when we have workflows if you have a large app for example, that has many rolls, it has a regular user, administrative users, or different parts of the app that are only accessible to certain users, that sort of things. Those workflows tend to become fairly complex fairly quickly. Similarly when we start to introduce real time data. If we’ve got web sockets or server. We want to be able to deal with that data that’s coming in. and be able to manage it in the same way. similarly if we’re fetching data from multiple endpoints, ABIM endpoints to build up a view. If you’re making four or five calls to collect data we need to coordinate between when this piece of data loads, we need to update this this thing when this piece of data comes in, we need to update this thing. So if you imagine all the things in a complex app like Facebook, you’ve got nav bar notifications, lists of users, real time messaging. All these things were the motivation for moving to Flux architecture. This is a high level overview of that architecture. And we’re going to talk about some of these pieces. I think the thing to note in this diagram is that data flows in one direction. So we never have an arrow pointing in one direction in any two of these boxes, and that’s something that’s very important. The unidirectional flow of data is a very cool piece. Being able to manage applications state in a predictable way.

Similar to Flux, the core concept is that we’re dealing with a transactional state model. So that we can see that in the example app. Essentially we have a state in some point in time, we dispatch an action, which is an object or entity that describes something in our app. We pass that through a reducer, whose job is to take an initial state, or a previous state, apply that action so we generate a new state so that we arrive then at the next state at our app. Its similar to a bank transaction. If we have a given state and a list of actions we can produce any state from those two things. Keep this in mind. This is kind of at the core of what gives us predictability. Also note that this is a serial on process from state to state linearly. We’ll come back a little bit later to talk about how we deal with like fetching data from an API.

We’ll start with actions. Actions are pretty simple; just a plain object, by convection they have a type, in this case the type is just a constant that evaluates to a string. We had some sort of payload, so this is all the data that comes with the action. In the case of the to-do list, we may have an action to add to the list. We need the content, the thing that the user will eventually do. When we dispatch an action, this is what we’re emitting. Just a plain serializable job descrip object. It serves as basically the record for state transaction. So the action says what happened. It’s the only source of information for the store. We’ll talk more about the meaning of the store later but it’s essentially our source of truth for applications state. If we go back to the Flux diagram, you can see that the actions are dispatched, and it’s the only way we can get data into the store. That means that when we start talking about components, the components are never say, mutating or changing the application directly, everything is dispatched through an action. Where actions describe the fact that something happened, there just data. They don’t deal with actually changing anything in the state. This is really what the job of reducers are. In Redux we have a singer reducer; a root reducer. It’s a function that takes its previous state object, as well as an action, and returns the next state. Obviously for a large app trying to manage everything in a single function should seem obvious that sustainable. We actually, we can compose reducers.

When we look at the store we’ll see an example of the state object, and we’ll look at it in the example app. We have data for searching the IED’s for previous searches, our lists of books, the actual entities that represent the data for an individual book. This gets quite large. Depending on the implementation, basically every implementation or most of the implementations of Redux provide a way to compose reducers. So we can break down our applications state and create smaller reducer functions that only deal with a slice of the state object. We may have a reducer for example, that only deals with books, a reducer that deals only with the collection or things related to the layout. We compose those into a single root reducer. There are a couple properties that are pretty important. They are invaluable characteristics of reducers so we can’t violate these constraints. One of them is that reducers must be pure functions.

If you’re not familiar with the term, a pure function is one that, given a set of inputs will always return the same output. Within that function, we can’t create any side effects. That means that for example we cannot mutate the state. The state object that comes in we can’t push data onto an array, we have to create a new object that contains the data or the changed data. They must not produce side effects. Meaning that, within a reducer we can’t call an API. we can’t do things like call non-pure functions like at random because those will cause our functions to be impure. We don’t get the same output if we call that function repeatedly with the same input. Those are hard constraints. I think for people who are new to Redux, that tends to be a point of aggravation when you say “why can’t I make an API call in a reducer” because you can’t. It’s important because the purpose of using this architecture is predictability, and the side effects are unpredictable, so we keep all the side effects outside the reducers.

The store we can think of as more or less as a data base on the client side. It’s our source of record for the application state. In Flux, where you could potentially have many stores, Redux has only one store. There can only be one, there’s only one state object, and we only deal with single objects. Our application state is a single object and its a plain object meaning we don’t have things that aren’t serializable. It doesn’t have functions or anything like that. This is all of the data we need to build our view in the application. This excludes transient UI states so we may have components say, if a user is filling out a form interview, we may have some form validation, things like that that. The user might need to submit that form before that day that is persisted. Not necessarily every piece of state in your application may be part of your application state. But generally we can think of things that are persistent. For example if you refresh the page, the data that will be there, or if we’re generating views on the server. These three things are working together to transition from state to state through our app. As the user is interacting we’re dispatching actions.

We’re going through reducers which take a state and an action, and produce a new state. We’ll repeat this process as the user interacts or as we receive events from the server. Anything that would result in state change. That process deals with with what’s happening; how things work. We also need to deal with the presentational aspect. We can divide all of the components of our app into two categories. If you’re familiar with react, everything is a component, a hierarchy of components, same with Angular and angular two. Our entire app starting with a root component is just a tree of components. In the Redux world, we have what are known as smart components . these are also known as container components. Generally, they’re concerned with how things work. These components that listen to the state changes. They’re going to be subscribing to the store.

In the case of NGRX they are going to be listening to the observable, and responding to the events that are being emitted when a state change occurs. Looking back at the diagram, every time we go from state one, to state two, to state three, we’re omitting an event that says the application state has changed. The components that are listening to the store and then take whatever part of the state that they care about and propagate that down into their components. In addition to listening to the store, they’re also the components that dispatch actions. They’re the components that know when to signal that something should change in the application state. And if you recall, we never touch the store directly, we never change the store directly we simply dispatch actions. They go through the reducer process and then the store will omit the next event indicating the store has changed. This process is happening continually as the user is interacting, or as other events that indicate state changing or a time or if we get a response back from the of it. We also have dumb components. Also known as presentational components, they tend to be stateless. They’re concerned with how things work. If you imagine angular material or material design components or something like bootstrap. Those are components that aim to be reusable. They’re not really concerned with the business rules of our application. They may be concerned with things like dates and calendars and stuff like that. They tend to be highly contestable, highly reusable. Those things are not dependent at all on our application state.

They get all their data from something else, from their parent. Something that knows, that something is our smart components. They also don’t dispatch actions. Any sort of change would be vented up through event emitters so that the smart components that compose the dumb components can be responsible for dispatching the actions. A typical app will tend to have more stateless components more dumb components, and often times these components will be farther down on the component hierarchy but it’s not always true that they’re not always children or that they don’t have smart components as children. For example, the material layout components, the side components is a dumb component, but it may in its containers have smart components that contain substantial part of that particular view.

There are some good articles that are included in some of the references that discuss a little bit more about our smart and dumb components. our entire app is a hierarchy and we’ve isolated all of the state change to the Redux process so that any time we want to make a change we can do so through the actions, and we simply wait until the sore emits an event that lets us know a change occurred, then we can simply re render our app. The component hierarchy is essentially the UI function. We can take some state, pass it to that route component and let that data propagate down, and that’s how we’re able to rebuild these views as we move through the app. I wanted to come back to async actions because this is one of the advanced topics that comes up. We’ve been talking about this process of moving from state to state. But we have to deal with if we make a request to an API that is a side effect. We don’t know when that request is going to be complete. We don’t know if that request is going to be successful. Something could go wrong on the server and we don’t get the data back. We need a way to deal with that unpredictability and not blow our nice synchronous transactional state model. The way to do that is through sync actions. To some degree, this is an implementation detail. How this is handled is influenced by the particular Redux library. NGRX for example, has a companion library called a effects, and its job is to take actions and transform them into other actions. If you read the original Redux documentation they talk about which is another mechanism that’s actually implemented in the NGRX library then moved it to the approach.

So terminology may come across that term middleware, but it’s an implementation detail. If we imagine that we have some action that represents an asynchronous action, like fetch posts, so if we want to fetch a list of blog posts from the server, this is inherently an asynchronous process. What we need to do is take this action and transform it it into a series of actions. So that our IU can respond, and render itself in a way that represents that process. This shows a number of states and were showing a simplified state object for these. Then the actions that we end up dispatching. If we emend starting with this object, we have not fetched anything yet. We have not initiated any API requests, theres no errors, were not loading anything and we have no post. So we can transform that fence post action, and initiate the request, then dispatch an action that indicates that we started the request. So our next state will indicate that we are currently loading. The rest of the application in terms of the view and the UI doesn’t care that we are waiting for a request.

At some point the request will complete, an it will either be successful and we’ll get the data back, in which case we can dispatch a success action and transition to state s3. In this case we have no error, were not loading and we have a list of our posts. In an alternate universe, where our request fails, we can issue the failure action and that will pass through the reducer, and the state will be that we are not loading, but we do have an error, and we have no post. If we look at the hypothetical user interface for those particular states we can see that before we initiate a request our button is active. The user clicks on the button, we dispatch our load post action which is transformed into the first action which is make the request, and at that point we have the state object which says that loading is true, so we know to render the loading text, and disable. When the request completes, we’re going to transition into one of two states based on the result of the request. So either we have our post and we can render them, or we don’t and we can change the state of the button and let the user try the request again. That’s how we deal with while the processes of fetching the data is an asynchronous process, our UI state remains synchronous and cereal, and again deterministic. In a testing scenario, we can disconnect all of our things that deal with making API requests and fetching data. And we can use fake data to transition our UI, to test our IU, if we wanted to do automated tests then we could stump out that data and all those requests and still be able to test the user interactions without actually having to worry about servers and those kinds of things that make UI testing difficult. I want to finish by talking a little about the particular implementation, NGRX.

NGRX was built RXGS so it’s powered by observables, it works very well angular, it seems to have a good ecosystem, and a fair amount of community support, tends to be more help and information for NGRX than some of the other implementations. The store is the primary piece, then there are a number of companion libraries depending on what you need to do. So effects is a side effect model for dealing with all of those action transformations and the other unpredictable pieces. Route store binds to the angular router so we can see the route path. We can see that in the example app. As we change, we have the URL information in the application state as well. The store deb tools is the instrumentation library which we can turn on for debugging and time travel debugging and things like that. I mentioned the reducers need to be pure functions. By itself, its left as an exercise to the implementer. There’s nothing built in to stop you from mutating the application state. You could bring in a library like a mutable JS if you want to enforce that you’re using a mutable types, then you’ll know you’ll get errors if you try to mutate.

This serves a similar purpose in that we can load it up in a development environment if you try to mutate state in your reducers. Generally speaking, i would recommend having something in place because it’s difficult to manage through discipline alone. Holding developers accountable. And again the example app showcases many of the capabilities as they relate specifically to NGRX. theres also angular Redux which i believe was formally NG2 redux, so angular bindings for angular 2, there’s also NG redux if you happen to be dealing with an angular 1 app. Those are bindings for the redux libraries.

  • Contact Us
  • Telephone: 888.679.2201
  • Address:
    BrieBug |
    12596 W Bayaud Ave Suite 201 | Lakewood, CO 80228