We would like the ability to group a series of actions to be dispatched with single dispatching functions used as handlers in various parts of our game. The only issue with that, is that animations and other design elements in our game require us to provide some temporal space between each of those actions in the series being fired.
This is something we can achieve by reaching for the Async
ADT provided by the crocks library. In order to allow for Async
s in our Redux flow we are going to need to create some middleware that can identify when an Async
is provided as an action, and then handle it appropriately .
Instructor: [00:00] Inside of our game component, we provide this map dispatch function, which includes the start function that dispatches an array of actions when called, starting with the start game action that generates and selects a collection of nine cards, followed by hide all cards that deselects and flips the cards.
[00:16] We finish with start turn to generate a hint for the player to guess, which we import in from our turn reducer. Currently, all three of these actions are fired, one right after the other, when the player clicks the start game button. Over in the Redux Devtools, we see that start turn was the last action fired.
[00:34] With a quick jump back to the hide all cards action, we see that this hint was populated by start turn while hide all cards removed the selected true from each card, which was added when start game fired, generating the cards with the selected attribute as we see in the actions div pane.
[00:51] Let's create some middleware that will give us the ability to control the time between each of these actions firing. In our middleware async file, as asynchronous code can be very volatile, we find this error action function that we can use to create an FSA in the case of an error occurring.
[01:07] Let's get to work by exporting as default from this file an async middleware function that plucks off the dispatch function from the store provided to us from Redux. Then return a binary curried function that first takes in a next continuation function followed by a given action.
[01:23] We'd like to check if this action is an instance of the crocks async type and handle it appropriately if it is. To perform this type check, we'll reach for the crocks' same type predicate function that compares a value to either a constructor or another value, returning true if they're the same type or false otherwise.
[01:41] We'll also bring in the crocks async constructor to have something to compare the action to.
[01:47] Downstairs, deep in the belly of our middleware function, we use is same type to compare the async constructor with the provided action. If it is an async instance, we know it's safe to call fork on the action. Fork takes two unary functions. The first will be run in the case of rejection, while the second is used if the async successfully resolves.
[02:07] To handle the unlikely case of a left or rejected instance, we'll build this simple composition by first bringing in the crocks compose helper function to build out a composition that provides the next function, the result of our action creator that takes in the rejection error as its payload.
[02:23] With this function, we can dispatch any errors that may occur. For the right or resolved, we just dispatch the provided action.
[02:31] If we don't have an async, we just pass it down the line with our continuation function. Now, we need to get this into our middleware chain, so over in our store file we need to pull it in from the async middleware file, then apply it to our middleware enhancer, allowing us to now handle dispatched async instances.
[02:49] Now, let's dispatch some sweet, sweet async actions by first pulling in the crocks async constructor into our game component in order to create some instances to dispatch.
[02:59] We want to delay the dispatching of the last two actions so we can reach for the resolve after async construction function, which takes a delay interval for the first argument and a value to resolve with as the second, which will be our actions in this case.
[03:13] For our intervals, we'll give the player five seconds to memorize the cards. To make sure that the flip animation has time to complete, we'll show the hint a little over half a second later.
[03:24] That's it. Now, when the player clicks start game, start game is fired, showing the cards in all their glory. Just five seconds later, our hide all cards action is dispatched, followed closely by our start turn, displaying the hint.