What the heck is a 'thunk'?

Thunk! (the redux kind)

Q: What is a ‘thunk’?

A: The sound your head makes when you first hear about redux-thunk.

Ok sorry, that was awful.

But seriously: Redux Thunk is a really confusing thing when you first hear about it. I think it’s mostly because of that word “thunk.” So let’s clear that up first.

thunk, n.

A thunk is another word for a function. But it’s not just any old function. It’s a special (and uncommon) name for a function that’s returned by another. Like this:

function not_a_thunk() {
  // this one is a "thunk" because it defers work for later:
  return function() {
    console.log('do stuff now');
  };
}

You already know this pattern. You just don’t call it “thunk.” If you want to execute the “do stuff now” part, you have to call it like not_a_thunk()() – calling it twice, basically.

redux-thunk

So how does this apply to Redux?

Well, if you’re familiar with Redux, you’ll know that it’s got a few main “concepts”: there are “actions”, “action creators”, “reducers”, and “middleware.”

At its heart, though, Redux is really simple. Actions are just objects – and they are expected to only be objects. They look like this:

{
  type: "USER_LOGGED_IN",
  username: "dave"
}

And, since it’s kind of annoying to build objects by hand all the time, Redux has “action creators” that build these things:

function userLoggedIn() {
  return {
    type: 'USER_LOGGED_IN',
    username: 'dave'
  };
}

Same action, but now you can “create” it by calling the userLoggedIn function.

Isn’t it kind of funny that Redux’s so-called “actions” don’t actually do anything? They’re just objects. Boring and simple and inert.

Wouldn’t it be cool if you could actually make them do something? Like, say, make an AJAX call, or trigger other actions? Because reducers are supposed to be “pure” (as in, they don’t change anything) we couldn’t put that work inside a reducer.

If you wanted an action to do something, that code would need to live inside a function…

It would be nice if an action creator could return that function instead of an action object. Something like this:

function getUser() {
  return function() {
    return axios.get('/current_user');
  };
}

If only there were some way to teach Redux how to deal with functions as actions…

Well, this is exactly what redux-thunk does: it is a middleware that looks at every action that passes through the system, and if it’s a function, it calls that function. That’s all it does.

The only thing I left out of that little code snippet is that Redux will pass two arguments to thunk functions: dispatch, so that they can dispatch new actions if they need to; and getState, so they can access the current state. So you can do things like this:

function logOutUser() {
  return function(dispatch, getState) {
    return axios.post('/logout').then(function() {
      // pretend we declared an action creator
      // called 'userLoggedOut', and now we can dispatch it
      dispatch(userLoggedOut());
    });
  };
}

Update: As rixman mentions in the comments, the getState function can be useful for deciding whether to fetch new data, or return a cached result, depending on the current state.

That’s about it. That’s what redux-thunk is for.

Set up redux-thunk in your project

If you have a project that already has Redux set up, adding redux-thunk is only a few steps.

First, install the package:

$ npm install --save redux-thun

Then, wherever you have your Redux setup code, you need to import redux-thunk and insert its middleware into Redux:

// You probably already import createStore from 'redux'
// You'll need to also import applyMiddleware
import { createStore, applyMiddleware } from 'redux';

// Import the `thunk` middleware
import thunk from 'redux-thunk';

// Import your existing root reducer here.
// Change this path to fit your setup ;)
import rootReducer from './reducers/index';

// The last argument to createStore is the "store enhancer".
// Here we use applyMiddleware to create that based on
// the thunk middleware.
const store = createStore
  rootReducer,
  applyMiddleware(thunk)
);

Learning React can be a struggle -- so many libraries and tools!
My advice? Ignore all of them :)
For a step-by-step approach, read my book Pure React.

Loved it! Very well written and put together. Love that you focused only on React. Wish I had stumbled onto your book first before I messed around with all those scary "boilerplate" projects.
— John Lyon-Smith
comments powered by Disqus