Redux Thunk Middleware and Async / Await

Look, Ma! No promises!

You can check out the full source code for this example over at its github repo. You can see it run in your browser here.

If you use thunk, you can use async thunks with no modifications to the thunk middleware

import 'babel-polyfill' // polyfill for regenerator runtime  
import 'whatwg-fetch'   // polyfill for window.fetch

import { applyMiddleware, createStore } from 'redux'

const thunk = store => {  
  const dispatch = store.dispatch
  const getState = store.getState

  return next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState)
    }

    console.log(action)
    return next(action)
  }
}

const reducer = (state = {}, action) => {  
  switch (action.type) {
    case 'fruit':
    case 'vegetable':
    case 'json':
    case 'error':
      return {
        ...state,
        [action.name]: action.value
      }
    default:
      return state
  }
}

const store = createStore(reducer, applyMiddleware(thunk))

const asyncThunkTest = () => {  
  return async (dispatch, getState) => {
    dispatch({ type: 'fruit', name: 'banana', value: 'yellow' })

    try {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts')
      const json = await response.json()
      dispatch({ type: 'json', name: 'posts', value: json })
    } catch (e) {
      dispatch({ type: 'error', name: 'error', value: e.message })
    }

    dispatch({ type: 'vegetable', name: 'carrot', value: 'orange' })
    console.log('state: ', getState())
  }
}

store.dispatch(asyncThunkTest())  

SHOW ME YOUR JSON

// console output
Object {type: "fruit", name: "banana", value: "yellow"}  
Object {type: "json", name: "posts", value: Array[100]}  
Object {type: "vegetable", name: "carrot", value: "orange"}  
state:  Object {banana: "yellow", posts: Array[100], carrot: "orange"}  

Sure, but let's say something blows up...

// console output
Object {type: "fruit", name: "banana", value: "yellow"}  
Fetch API cannot load httpjjjs://jsonplaceholder.typicode.com/posts. URL scheme must be "http" or "https" for CORS request ....  
Object {type: "error", name: "error", value: "Failed to fetch"}  
Object {type: "vegetable", name: "carrot", value: "orange"}  
state:  Object {banana: "yellow", error: "Failed to fetch", carrot: "orange"}  

A Beautiful Lie

// Generated ES5 for our innocent little asyncThunkTest function
var asyncThunkTest = function asyncThunkTest() {  
  return function () {
    var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee(dispatch, getState) {
      var response, json;
      return regeneratorRuntime.wrap(function _callee$(_context) {
        while (1) {
          switch (_context.prev = _context.next) {
            case 0:
              dispatch({ type: 'fruit', name: 'banana', value: 'yellow' });

              _context.prev = 1;
              _context.next = 4;
              return fetch('https://jsonplaceholder.typicode.com/posts');

            case 4:
              response = _context.sent;
              _context.next = 7;
              return response.json();

            case 7:
              json = _context.sent;

              dispatch({ type: 'json', name: 'posts', value: json });
              _context.next = 14;
              break;

            case 11:
              _context.prev = 11;
              _context.t0 = _context['catch'](1);

              dispatch({ type: 'error', name: 'error', value: _context.t0.message });

            case 14:

              dispatch({ type: 'vegetable', name: 'carrot', value: 'orange' });
              console.log('state: ', getState());

            case 16:
            case 'end':
              return _context.stop();
          }
        }
      }, _callee, undefined, [[1, 11]]);
    }));

    return function (_x2, _x3) {
      return _ref.apply(this, arguments);
    };
  }();
};

May your source maps never fail...

comments powered by Disqus