Redux: Wrapping dispatch() to Recognize Promises

Dan Abramov
InstructorDan Abramov
Share this video with your friends

Social Share Links

Send Tweet

We will learn how to teach dispatch() to recognize Promises so that we can move the async logic out of the components into asynchronous action creators.

[00:01] The receive todo action creator is not very useful by itself because anytime we call it, we want to fetch the todos first. Also, they accept the same argument so it would be great if we could group this code into a single action creator.

[00:17] I'm opening the file where I define the action creators and I'm adding a new import there. It will make all functions in the API module available on the API namespace import object.

[00:31] I'm adding a special kind of action creator that I'm going to call in an asynchronous action creator. It takes filter as an argument and it calls the API fetch todos method with it. I'm using the promise the method to transform the result of the promise from the response to the action object generated by received todos given the filter and the response.

[00:58] Received todos return an action object synchronously but fetch todos returns a promise that resolves through the action object. I can stop exporting receive todos because I'll change the components to sue fetch todos directly.

[01:15] I'm going back to my component file and I can use the fetch todos prop injected by connect which corresponds to the new asynchronous fetch todos action creator. I'm removing the direct import of fetch todos function from API because from now on I'll be using the fetch todos action creator, which is injected into the props by connect.

[01:40] Let's take another look at what happens in the action creators. The fetch todos action creator calls the fetch todos function from the API but then it transforms its result into a Redux action generated by receive todos.

[01:57] However, by default, Redux only allows dispatch in plain objects rather than promises. We can teach it to recognize promises by using the same trick that we use for login every dispatch.

[02:11] A login into dispatch is the function we wrote before that creates the dispatch from the store and returns a new version of dispatch that logs every action and the state. In a similar fashion, I can create a function called at promise support the dispatch that takes the store and returns a version of dispatch that supports promises.

[02:35] First, we will write the raw dispatch function at it is defined on the store so that we can call it later. We return a function that has the same API as a dispatch function that is, it takes an action.

[02:51] We don't know if the action is a real action or a promise of action, which hack if it has a then method that is a function, which is a way to tell if something is a promise. If it is a promise, we wait for it to resolve to an action object that we pass through raw dispatch.

[03:11] Otherwise, we'll just call raw dispatch right away with the action object we received. Now, we can dispatch both actions and promises that resolve to actions. Finally, I need to use the function I just wrote to write the dispatch function one more time before returning the store to the app.

[03:32] If I run the app now, I will still see the receive todos action being dispatch when the response is ready. However, the component uses a more convenient API that encapsulates the asynchronous logic in the asynchronous action creator.

[03:49] One thing to remember is that the order in which we override the dispatch function is important. If we change it, the action will first be printed and then the promise will be processed so action type is undefined and we see the promise instead of the action, which is not very useful.

[04:08] This is why I'm changing the auto back so that the promises are resolved before the action is locked. Let's recap how we added the promise support to the dispatch function.

[04:20] We override store dispatch two times. Once to add the login and second time to add the promise support. The function that adds the promise support accepts the store an argument and it reaches the previous value of the dispatch function.

[04:37] It returns a function that looks like a dispatch function because it accepts the action but if the action is not really an action but a promise, we're going to wait for that promise to resolve to the real action, which will pass through raw dispatch.

[04:53] Finally, if the action is not a promise, we'll just dispatch it right away by calling raw dispatch. Let's see what happens when we try to dispatch the result of call and fetch todos asynchronous action creator.

[05:08] First of all, it calls the fetch todos function from the API module, which I import as a namespace import. It waits for the promise to resolve with the response and rather than return the response, it returns the result of calling receive todos action creator, which returns an action object.

[05:31] This is why fetch todos, itself, returns a promise that resolves through the action returned by receive todos. The wrap dispatch function recognizes that this is a promise and not an action so it waits for the promise to resolve and passes it on through the raw dispatch function with dispatches the action object returned by receive todos.

[05:56] I remove the export from receive todos because it is only used as part of the asynchronous fetch todos action creator. Being a named expert, it becomes available as part of the actions object, which is the namespace import invisible todo list.

[06:14] The actions object gets passed as a second argument through the connect function. This has me call these props fetch todos from my component without thinking whether it is backed by a synchronous and an asynchronous action creator.