Redux Toolkit comes baked in with support for redux-thunk as the recommended way to model asynchronous actions. Thunks can take a bit of getting used to. A typical thunk looks something like this:
function myAction() {
return function myActionThunk(dispatch) {
dispatch({ type: "myAction" });
}
}
// somewhere in the UI code
dispatch(myAction())
So you dispatch a function that is handled and executed by the middleware, and that function receives its own dispatch
argument that it can use to send multiple actions into the redux store over time.
Thunks are most commonly used when dealing with API calls where you'd send an action to indicate an API call was started and another once the response is received. We model that here with extraReducers
and the builder API listening for cart/checkout/pending
and cart/checkout/fulfilled
actions, which we dispatch from our single checkout thunk.
It's also worth noting why I'm calling a function to return my thunk rather than exporting the thunk function directly. First of all, you can export it directly, but it's common to wrap it like this, because you often want to pass in arguments from your UI layer that ultimately impact how the thunk is used.
For example you could imagine calling dispatch(checkout(items))
and then passing those items into your API call in your thunk.
Another thing not mentioned in the lesson is that thunks take a second argument which is a getState
function. This is useful in cases where your async call needs access to the current state object.
Jamund Ferguson: [0:00] While we can only dispatch action objects into the Redux store, Redux Toolkit comes preloaded with a special middleware called Redux Thunk that allows you to dispatch functions.
[0:09] These special thunk functions can then dispatch multiple actions over time. They're super useful in all kinds of situations. Inside of cartSlice.ts, go down to the extraReducers section and let's add another case. This one will be for cart/checkout/fulfilled. Then in that reducer, we'll set the checkout state to "READY".
[0:29] Below our createSlice function, type export function checkout() and inside of that function we're going to return another function which we'll call checkoutThunk. CheckoutThunk takes for its argument dispatch and inside of the function we'll dispatch an action with a type: "cart/checkout/pending". If we want to type that dispatch argument, we can pull in AppDispatch from app/store and use that for the type.
[0:54] In cart.tsx we're going to import the checkout method from ./cartSlice, then down in the onCheckout() function instead of dispatching this custom action, let's dispatch the results of our checkout function.
[1:06] If we head back to our shopping cart and click Checkout, you can see that the state changes continue to be applied properly. If we go back into cartSlice, we can now do something really interesting. Let's add a timer of 500 milliseconds and inside that setTimeout we're going to dispatch another event. This one of type "cart/checkout/fulfilled". Within this thunk we're able to dispatch multiple actions and we're able to do it over time.
[1:30] Let's go back into our browser. We click Checkout. You see that it's pending for a few hundred milliseconds, and then it goes right back into ready mode.