Reusing Redux action creators
Tags: Software Development, API, Sketch, Redux, React

While looking for best practices to do API calls with Redux, I came across the real world example which included a really neat way to make API calls. Based on this example, I created my first action to fetch a single resource, which in this case is a document from the Sketch Cloud API:

const fetchDocument = documentID => {

return {

type: 'CALL_API',

payload: {

types: [FETCH_DOCUMENT_REQUEST, FETCH_DOCUMENT_SUCCESS, FETCH_DOCUMENT_FAILED],

endpoint: `/documents/${documentID}`

}

}

}

As you can see, this function requires the id of the document we’d like to fetch and uses it to create an action with a type of ‘CALL_API’. We can simply dispatch this action if we want to make the request.

Making the actual request

We can make an API request by dispatching an action with the ‘CALL_API’ type, but where does the actual request take place? Well, this is happening in a middleware of course. There’s a middleware which checks every dispatched action, and if it’s of the type ‘CALL_API’ it’ll create a request for us! It’ll also look at the ‘types’ property in our action payload and in turn dispatches the REQUEST, SUCCESS and FAILED actions based on the outcome of the request.

Generating lots of boilerplate

Awesome! 🎉 Everything’s in place to make API requests, but after adding lots of actions to make different requests I ran into a problem. While this worked pretty good, it resulted in a lot of boilerplate code. If you add other types of requests like POST, PUT and DELETE, the number of functions you have to define quickly add up. We need a way to make this less boilerplate heavy, so we’re able to quickly implement lots of different calls.

Partial application to the rescue!

Partially applying a function means creating a new function by pre-filling some of the arguments to the original function. - functional programming jargon

In our case using bind would provide us with the ability to partially apply these action creators, creating new, more specific actions. So, instead of the very specific fetchDocument action creator, we’re now writing a more general creator for performing a GET request. Something like this:

const fetch = (responseNormalizer, createEndpoint, resourceType, resourceID) => {

return {

type: 'CALL_API',

payload: {

types: [FETCH_REQUEST, FETCH_SUCCESS, FETCH_FAILURE],

endpoint: createEndpoint(resourceType, resourceID),

normalize: responseNormalizer,

requestOptions: {

method: 'get',

headers: {

'Content-Type': 'application/json'

}

}

}

}

}

Where responseNormalizer would be some function to normalize the API response and createEndpoint some function to generate an endpoint based on the resourceType and resourceID. And now, every GET request we’d like to make, could partially apply this fetch function:

const fetchDocument = fetch.bind(null, documentNormalizer, documentEndpointCreator, 'documents')

Notice that we can define (or partially apply) each parameter except for the id. This leaves us with an action creator for fetching a single document which only requires an id as its parameter, and it only took us a single line of code. If we want to fetch a document which has an id of ‘1’ we can simply dispatch the following action:

dispatch(fetchDocument('1'))

Food for thought

Because Redux actions creators are just plain functions, we can use the bind function on Redux action creators to generate more specific actions using partial application. But this leaves me wondering, what other cases for this technique could we come up with? 🤔