Navigate back to the homepage
📚 Books

Understanding Redux Middleware by understanding redux-thunk

Mark Pollmann
January 31st, 2018 · 1 min read

There has always been the notion that Redux middleware is some kind of black magic but the idea behind it is actually quite simple and elegant.

The Main Idea

Before reaching reducers actions can be manipulated with global functions called middleware. They can do whatever they want to the actions: log them to the console, change properties or stop some of them entirely. If you already know ExpressJS middleware you know Redux middleware, it’s the same idea. Every action flows through the registered middleware functions, one after the other and in order of registered middleware.

For example, if this is the code of our store:

1const store = createStore(Reducer, applyMiddleware(thunk, myCustomMiddleware))

then actions flow through thunk first, then through myCustomMiddleware and then get processed by the reducers.

Some Examples of Custom Middleware

Let’s take a very simple middleware. console.loging the action and then letting the action pass to the next middleware.

1function loggerMiddleware() {
2 return ({ dispatch, getState }) => next => action => {
3 console.log(action)
4 return next(action)
5 }

Now let’s write one that stops all actions from further processing:

1function stopperMiddleware() {
2 return ({ dispatch, getState }) => next => action => {
3 console.log("You shall not pass!")
4 }

As you can see, if the call to next is missing the flow to the next middleware (and later the reducers) stops.

Now let’s write one that, no matter what the action is, overwrites it with a another, fixed type of action:

1function loggerMiddleware() {
2 return ({ dispatch, getState }) => next => action => {
3 return next({ type: "trololo" })
4 }

I hope you get the idea.

Redux Thunk

Now that we understand what’s going on, let’s take a look at the redux-thunk library code.

1function createThunkMiddleware(extraArgument) {
2 return ({ dispatch, getState }) => next => action => {
3 if (typeof action === "function") {
4 return action(dispatch, getState, extraArgument)
5 }
7 return next(action)
8 }
11const thunk = createThunkMiddleware()
12thunk.withExtraArgument = createThunkMiddleware
14export default thunk

Yup, that’s all the code there is.

Let’s see, what does if (typeof action === 'function') mean, actions are always plain objects, aren’t they? The ones that reach reducers yes, and that’s the whole trick. Redux-thunk intercepts these cheating function-actions (that are created by passing dispatch() a function instead of an object), injects the three arguments and runs them. Actions that pass redux-thunk are (or should be) plain objects again. If not, somebody messed up and redux will complain.

Once you wrap your head around the functional programming style of returning functions from other functions and passing them around it will make more sense.

Thanks for reading!

More articles from Mark Pollmann

The this keyword in Javascript

Somebody has to write about it

September 5th, 2017 · 2 min read

Notes on Jan Koum's talk at Stanford

An interesting talk about WhatsApp's beginnings

August 20th, 2017 · 1 min read
© 2020 Mark Pollmann
Link to $ to $ to $