1.0.4 • Published 7 months ago

auto-thunk v1.0.4

Weekly downloads
32
License
MIT
Repository
github
Last release
7 months ago

AutoThunk

Enhanced version of redux-thunk that provides a powerful and short syntax to write async action creators.

  • backward compatible, drop in replacement of redux-thunk
  • No dependencies

Motivation

Working with RESTful apis, async action creators are repetitive to write. In most cases, we just need to make a request and send an action passing the response data to the Redux store. AutoThunk provides a short syntax to quickly connect your endpoints with the store.

Installation

npm i --save auto-thunk

Usage

Configure and apply the middleware:

import autoThunkMiddleware from 'auto-thunk'

const autoThunk = autoThunkMiddleware({
  httpClient: axios.create(),
  errorHandler: error => { ... }, // Default error handler
  log: <myLogFunction>,
  track: <myTrackFunction>,
})

const store = createStore(<reducers>, <initialState>, applyMiddleware([autoThunk]))

Write your action creators

Syntax

const getFoos = data => ({
  // Action as a string or an object or an array.
  action: <action>,
  // Request parameters (If an object is passed, it will be passed directly to the httpClient)
  request: ['<http client method>', '<endpoint>', <body>],
  // Log function will be called with '<identifier>' as the first argument, and the response/error as the second argument.
  log: '<identifier>',
  // Track function will be called with the given argument.
  track: <track function argument>,
  // Automatically transform the body data to formData.
  bodyType: 'formData',
  // Override the default error handler if needed
  errorHandler: error => { ... }
})

Examples

Basic example
export const getFoos = () => ({
  action: 'ADD_FOOS',
  request: ['get', '/foos']
})

The equivalent with redux-thunk alone would be:

import httpClient from 'myHttpClientInstance'
export const getFoos = async () => {
  return (dispatch) => {
    try {
      const response = await httpClient.request({
        method: 'get',
        url: '/foos'
      })
      dispatch({
        type: 'ADD_FOOS',
        data: response.data
      })
    } catch (error) {
      // some logic for error handling
    }
  }
}
Other examples
export const deleteFoo = ({ id }) => ({
  action: { type: 'DELETE_FOO', data: id },
  request: ['delete', `/foos/${id}`]
})
// Dispatched action ==> { type: 'DELETE_FOO', data: <id>}


// Dispatching several actions:
export const updateFooColor = ({ color, name, id }) => ({
  action: [
    { type: 'SET_UPDATED_FOO', version: 'v2' }, // Dispatched action ==> { type: 'SET_UPDATED_FOO', data: <response.data>, version: 'v2'}
    { type: 'UPDATE_FOO_COLOR', data: { color } }, // Dispatched action ==> { type: 'SET_UPDATED_FOO', data: { color } }
  ],
  request: ['put', `/foos/${id}`, { color, name }]
})


// With logging and tracking and custom error handler:
export const createFoo = ({ name, color }) => ({
  action: 'ADD_FOO',
  request: ['post', '/foos', { name, color }],
  track: { event: 'CREATION_OF_FOO' }, // Track function will be called with { event: 'CREATION_OF_FOO' }
  log: 'createFoo', // Log function will be called with 'createFoo' as the first argument, and the response/error as the second argument.
  errorHandler: error => console.log(error)
})
// Dispatched action ==> { type: 'ADD_FOO', data: <response.data>}

// Using request as an object:
const postSomeStuff = ({name, cancelToken}) => ({
  request: {
    method: 'post',
    url: `/stuff`,
    data: { name },
    cancelToken
  }
})

// Making requests without dispatching any action:
export const getBars = () => ['get', '/bars']
export const getBar = ({id}) => ['get', `/bars/${id}`]