Redux Thunk

What the thunk?

In computer programming, a thunk is a subroutine used to inject an additional calculation into another subroutine. Thunks are primarily used to delay a calculation until its result is needed, or to insert operations at the beginning or end of the other subroutine. They have a variety of other applications in compiler code generation and modular programming.

 

The term originated as a jocular derivative of "think".

Thunky Redux

Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.

redux-thunk is a plug-in to extend redux (via middleware).

TL;DR it lets you do async stuff

Thunktastic Diagram

Installing redux-thunk

1. npm i redux-thunk

2. Wherever you are calling createStore:

import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

/**
 * Build a Redux Store and apply middleware
 */
function configureStore() {
  const store = createStore(
    rootReducer,
    // enable the dev tools extension if you have it installed
    compose(
      applyMiddleware(thunk),
      window.devToolsExtension ? window.devToolsExtension() : f => f
    )
  );
  return store;
}

export default configureStore;

What does a thunk look like?

An action creator...that returns a function...that can dispatch other actions.

function incrementIfOdd() {
  // the function has two params:
     // dispatch calls the reducer with an action
     // getState fetches the current Redux state from the store
  return (dispatch, getState) => {
    const { counter } = getState();

    if (counter % 2 === 0) {
      return;
    }

    dispatch(increment());
  };
}

What does a real thunk look like?

export function fetchTodosRequest() {
  // THUNK!
  return async (dispatch, getState) => {
    // dispatch an action describing that we are making a request
    dispatch({ type: FETCH_TODOS_REQUEST });

    try {
      // call the API (async operation)
      const todos = await fetchTodos();

      // dispatch a success action if it works
      dispatch(fetchTodosSuccess(todos));

    } catch (err) {
      // dispatch a fail action if API call fails
      dispatch(fetchTodosFail(err));

      // manually reject the promise
      return Promise.reject();
    }
  };
}

What if I need to wait for a thunk to finish?

async componentDidMount() {

  // before component renders, fetch todos first
  try {
    await this.props.fetchTodos();
  } catch(e) {
    return;
  }

  // if we get here successfully, we're done loading!
  this.setState({
    loading: false
  });
}

The following code works as long as your thunk returns a Promise (async functions do this automatically)

Check out the "thunk" branch on my React Redux Data Flow app here

Additional Resources

Redux Thunk

By Michael Hueter

Redux Thunk

How to use thunks to manage asynchronous code in Redux.

  • 1,301