Redux Thunk Utils

May 30, 2019

This is a piece of code that was usefull for me while I was using redux and redux-thunk@1.x Some people found it usefull when I showed, so I am sharing it here.

It is basically a shortcut for writing actions and async api api requests/actions.



// Action shortcut
export function makeActionCreator(type, ...argNames) {
  let action = function(...args) {
    let action = { type };
    argNames.forEach((arg, index) => {
      action[argNames[index]] = args[index];
    });
    return action;
  };

  return {
    action,
    type
  };
}

// Usage
export const ENTER_CHAT_ROOM = makeActionCreator("ENTER_CHAT_ROOM", "room");



With our action generator utility, we further extend it for redux-thunk usage.

A common pattern arised most of the times I used thunk, so I abstracted it into a function. This way I had to write less code, but if needed the action could be further extended by chaining 'thens' and 'catchs' on the returning promise.



export function asyncCreator(api, actionName, resourceName) {
  let actionNames = ["REQUEST", "SUCCESS", "FAILURE"].map(
    stage => actionName + "_" + stage
  );
  let request = makeActionCreator(actionNames[0], "parameters").action;
  let success = makeActionCreator(actionNames[1], resourceName).action;
  let failure = makeActionCreator(actionNames[2], "error").action;

  let action = function(...parameters) {
    return function(dispatch) {
      dispatch(request(...parameters));

      return api(...parameters)
        .then(function(response) {
          dispatch(success(response.data));
        })
        .catch(function(error) {
          dispatch(failure(error));
          throw error;
        });
    };
  };

  return {
    REQUEST: actionNames[0],
    SUCCESS: actionNames[1],
    FAILURE: actionNames[2],
    request,
    success,
    failure,
    action
  };
}

// actions.js - Usage

const fetchUsersApi = fetch("/users");

export const fetchUsers = asyncCreator(fetchUsersApi, "FETCH_USERS", "users");

// reducer.js

import { combineReducers } from "redux";
import fetchUsers from "./actions";

function loading(state = false, action) {
  switch (action.type) {
    case fetchUsers.REQUEST:
      return true;
    case fetchUsers.SUCCESS:
    case fetchUsers.FAILURE:
      return false;
    default:
      return state;
  }
}

function error(state = "", action) {
  switch (action.type) {
    case fetchUsers.FAILURE:
      return action.error;
    default:
      return state;
  }
}

function users(state = [], action) {
  switch (action.type) {
    case fetchUsers.SUCCESS:
      return action.users;
    default:
      return state;
  }
}

export default combineReducers({
  loading,
  error,
  users
});



These helper functions made my life with redux and redux-thunk much easier as long declaration files became short one.

Hope this can also help you out!