How Redux Thunk Works
Redux thunk has been quite popular as its easiness to handle asynchronous action dispatch in redux store. Redux Saga
is awesome, but sometimes, especially for a medium or small size project, there is no need for us to involve Saga
as it requires more consideration and structuring. Moreover, for project in medium scale, it’s not worth involving Redux-Saga as learning curve for concepts and usage of generator function and side-effect is much more than Redux Thunk.
createStore in Redux
1 | export default function createStore(reducer, preloadedState, enhancer) { |
Let’s go to key point right now.
1 | return enhancer(createStore)(reducer, preloadedState) |
Hence, enhancer
will be a higher order function. AndcreateStore
, reducer
and preloadState
are passed as parameters.
When creating redux store
1 | const thunk = createThunkMiddleware() |
As ya can see, applyMiddleware(thunk)
will executed like applyMiddleware(thunk)(createStore)(reducer, preloadedState)
.
So what exactly happens in applyMiddlewared
? Go deeper.
1 | // applyMiddleware from redux |
Destructuring assignment tells us thunk
will be passed to middlewares in applyMiddleware
as an array.
After a few steps of execution, we go to the following line:1
chain = middlewares.map(middleware => middleware(middlewareAPI))
middlewareAPI
is an Object with getState
and dispatch
as its properties. Here, dispatch
property is different from local variable dispatch
. The local variable dispatch
is the real dispatching action from redux-store which we can dispatch action and change the corresponding data structure in redux store. Yet, dispatch
property refers to a function which accepts action
as a parameter.
So one thing left.
const thunk = createThunkMiddleware()
thunk
is the middleware we create and pass to applyMiddleware
as a parameter.1
2
3
4
5
6
7
8
9
10
11function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
See, it’s a functionality loaded of simplicity and elegance. thunk
is a function accepts middlewareAPI
as the result we have already got from our previous walking through on applyMiddleware
. We have missed one important thing, but I don’t wanna put too much elaboration on that right now. Just long story short on the following line:1
2chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
As we decorate our middlewares, we chain’em up by invoking compose function. compose
is from redux/compose
. The purpose of this functionality is to chain up all functions and execute them in sequence. But here, there is only one middleware, composing it up will only return this function.
After composing it, as we can see, store.dispatch
will be passed as middleware’s parameter. store.dispatch
is corresponding to next in createThunkMiddleware
.
Alright, after the aforementioned process is done, we expose a function like:1
2
3
4
5
6(action) => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
}
If action is like original Action Object, we directly dispatch them as usual.
If action turns out to be a function, dispatch
, getState
, extraArgument
will be passed into functionality as its three parameters, and do the following steps. You can put your asynchronous logics into your self defined functionality, when your final result is returned from backend, you are allowed to dispatch
your real action Object.