Invariably, the question comes up when talking about Redux, is how does one handle asynchronous operations. 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 gets handled properly.
The main thing with Thunk middleware is that it allows us to return a function instead of an action, and then this function can encapsulate the async operation and dispatch the appropriate action when the operation is complete.
To use Thunk middleware, we need to first install the MBM package. From the command line, MPM Install--Save Redux-Thunk. Now that this is installed, let's go into our main app file, and let's go ahead and import Redux Thunk. Import Thunk from Redux Thunk, and from here, we'll hop down to our config block. We're going to add this in our Create Store with Call as an item in the second parameter.
We're now initializing our store to use the Thunk middleware. Let's hop into our Category State, and let's wire this up. I'm going to create a URLs map here, because we're going to be making an http call. We'll point this to data/categories.json, and because we are making a few asynchronous operations, let's inject http and $q.
We'll annotate this with ng-inject, and then I'm going to create one helper function real quick, just an extract function that pulls a result and then returns result.data. This is just a convenience method that I like to use.
Within Get Categories, we're no longer going to return an action item directly. We are going to return a method that in turn returns a method. This is actually what a thunk is. It's a function that encapsulates another function to get called later. Within this function, it takes two parameters, dispatch and getState.
Because we have access to the getState method, we can pull the categories off of getState using destructuring. We're actually going to simulate caching, so we're going to check and see if we have categories already, if the store's been initialized with them, or has been placed in them.
If it has, we're just going to go ahead and resolve the categories that we have, so q.win, and then from there, within the then block, we're going to use the dispatch method to dispatch an action item of type getCategories and payload of categories.
If the Categories collection is empty, then we're going to make an ACTP call to go ahead and get them. We'll call ACTP.get. We'll pass in our fetch URL, then we'll extract the data. From there, we will use the dispatch method again, and we'll create a new action item, also with getCategories, but in this case, we are going to set the payload to equal our data from the server.
We can go ahead and delete this line here. We no longer need it. Let's hop into the browser. It looks like everything is working. Let's just double check. Let's pull up the JSON file. Let's update this to Development!! It does not look like that's pulling that in, the reason being is that because we're using caching, we need to take off the initial state, or it'll always just return these initial categories.
Let's set that to an empty array. Let's delete this, and back into the browser, let's refresh. Now you can see that we're pulling in the data from our JSON file, so we can close this.
Let's do a quick review. We updated getCategories to not return an action item but return a function that when the async operation has completed, it calls dispatch for us. Let's do the same exact thing for Bookmarks.
Before we forget, let's get rid of our initial bookmarks, initialize this to an empty collection, and then from here, let's create our URLs map, and we will create a fetch URL of data/bookmarks.json. Let's update the bookmarks actions to pull in $http and $q. Let's set up our extract helper function.
Let's update this to return our method here, so dispatch and getState. Within this, we're going to pull the bookmarks off of our application store using destructuring yet again. We'll check and see if we have bookmarks or not.
If bookmarks length, let's go ahead and use q.when to resolve the collection, and then we will dispatch our action at this point, with a payload of bookmarks.
If there is nothing in the collection, we will go ahead and make the http call. We'll get urls.fetch, then we will extract the data, and from here, we will use that data parameter to dispatch a new action item of getBookmarks with payload=data.
Let's hop into the browser and make sure that everything is loading. Refresh, and there we have it. Let's do a quick review.
We did an MPM install of Redux Thunk. Within our app.js, we imported Thunk from Redux Thunk, and we updated our create store with method to use and initialize our store with the Thunk middleware.
From there, we updated getCategories to not return an action item, but a method that takes a dispatch and getState parameter that we can then use to get our categories, will check it, and if it exists, we'll use the dispatch method to then dispatch our action item.
If not, we will make our remote call, and on the return of that, then we will use dispatch. Because we're doing caching, we initialized our initial categories to just an empty array, and the same thing for getBookmarks.
We updated it to return a function and not an action item, and then we used getState and dispatch to handle the asynchronous nature of this operation. This is how we handle async operations within our Angular application using Thunk middleware.