Reducing redux-thunk boilerplate
Asked Answered
D

2

11

When writing redux-thunk functions, known as thunks there is allot of boilerplate that could be easily abstracted away. For example in most of our async API calls we are doing the following, without any side-effects:

export const LOGIN_REQUEST = 'my-app/auth/LOGIN_REQUEST';
export const LOGIN_RECIEVE = 'my-app/auth/LOGIN_RECIEVE';
export const LOGIN_FAILURE = 'my-app/auth/LOGIN_FAILURE';

// ... reducer code here

export function login(loginHandle, password) {
  return (dispatch, getState, api) => {
    dispatch({ type: LOGIN_REQUEST });

    api.post('/auth/login', { loginHandle, password }).then(
      response => dispatch({ type: LOGIN_RECIEVE, response }),
      error => dispatch({ type: LOGIN_FAILURE, error })
    );
  };
}

Easy! Although as this covers at least 70% of our requests I'm sure there is an elegant way to abstract away allot of the above code to something like this (pseudo code):

export function login(loginHandle, password) {
  return (dispatch, getState, api) => api('POST', LOGIN_REQUEST, '/auth/login', { loginHandle, password });
}

When we need to check the state and other side effects we can go back to a proper thunk. Although for most cases... we could cut this down?

Any elegant ideas?

Downing answered 25/5, 2016 at 8:16 Comment(0)
L
9

Redux Thunk lets you inject a custom argument since 2.1.0.

const api = createApi() // you would write this function
const store = createStore(
  reducer,
  applyMiddleware(thunk.withExtraArgument(api))
)

// your action creator:
function fetchUser(id) {
  return (dispatch, getState, api) => {
    // you can use api here
  }
}

In the future, if your thunks get too complicated, you might want to consider redux-saga or redux-observable.

Laurettelauri answered 25/5, 2016 at 12:5 Comment(3)
As you can see in my question above we are already injecting our api as an argument. The issue was more related to reducing the request, receive, fail boilerplateDowning
Sorry, I thought “something like this (pseudo code)” meant you were’t aware of withExtraArgument() and just happened to suggested a similar API.Laurettelauri
If you’d like to reduce request/receive/fail boilerplate, check out github.com/acdlite/redux-promise.Laurettelauri
P
0

Unfortunately, there is no common way in redux community to approach this exact problem. I personally feel that people should not be afraid to write their own custom wrappers around redux to deal with exactly such situations.

I created a library called redux-tiles, which actually has almost exact API as you want :) For instance, you code will look like the following:

import { createTile } from 'redux-tiles';
const login = createTile({
  type: ['user', 'login'],
  // params is an argument with which you call an action
  fn: ({ api, params }) => api('POST', '/auth/login', params),
});

As you can see, there are no constants here, as well as a reducer. These things are created automatically, so you don't have to do it, as well as testing it. There are other features, like nesting (so same function will be applied for fetching items by id, for instance, but they will be correctly updated inside reducer) and caching. You can checkout examples here.

Redux-saga is a good thing too, but it is more if you need some sort of reactivity, if you need more traditional approach, just a little bit more convenient way to describe your actions and combine them, without repeating yourself, then I find my library to be a perfect match.

Panamerican answered 4/9, 2017 at 0:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.