Network Action
A simple wrapper around Fetch API, ironing out some of its kirks, and adding Redux action dispatching.
Note: this is a ES6-only module, and must be used in a ES6 environment, or transpiled as needed.
Usage demo
import networkAction from '@gluedigital/network-action'
export const someApiAction = (username, password) => networkAction({
action: 'login',
url: 'http://localhost:3000/oauth/token'
body: { username, password }
})
Features
These are the main differences from using raw fetch:
- A loading action will be dispatched when starting the request, and a success/error one when finished.
- Automatic handling of posting JSON body, and defaults to parsing response as JSON.
- If you have a 'user' reducer with a 'access_token' key, it will be added to each request.
- When used with Universal Scripts on the server, it will passthrough any received cookies.
Options
The following options can be used:
Key | Type | Meaning |
---|---|---|
url | string | The absolute url that we want to fetch. |
baseUrl | string | A prefix that will be added to the url before using it. Useful for default values. |
action | string | Redux action type prefix. If action is 'foo', we will dispatch 'foo/loading' and one of 'foo/success' or 'foo/error'. |
body | any | The body of the HTTP request. Can be a FormData, string, or JSON object. |
token | string | An access_token to override the one retrieved from the store. |
noReject | bool | On error responses, don't reject the promise (to prevent unhandled rejections). |
processBody | func | Function to process the body before adding it to the request. |
processRequest | func | Function to process the request before sending it. |
processResponse | func | Function to process the response before handling it and dispatching the success action. It can reject it too by throwing. |
meta | any | Any extra info you want to associate to this request. Will be dispatched on the loading/success/error actions. |
fetchOptions | object | Extra raw options to pass to fetch(). |
Defaults
If you want to make multiple calls with some common settings, like when creating actions related with the same server, you can use this pattern:
import networkAction from '@gluedigital/network-action'
const fooNetworkAction = networkAction.withDefaults({
baseUrl: 'https://example.com/foo'
fetchOpts: { credentials: 'include' }
})
export const someFooApiAction = (username, password) => fooNetworkAction({
action: 'login',
url: '/oauth/token'
body: { username, password }
})
The withDefaults
helper adds the options passed to it as defaults for the next calls, and takes care to mix the fetchOpts properly.
Reducer
We provide a ready-made reducer for the actions dispatched by this library. You can use it like this:
import networkAction from '@gluedigital/network-action'
const myReducer = combineReducers({
loadFoo: networkAction.reducer('loadFoo'),
saveBar: networkAction.reducer('saveBar')
})
Additionally, networkAction.reducer
may take a list of actions that will share the same state key (and, therefore, overwrite its previous value when one of the actions is dispatched).
import networkAction from '@gluedigital/network-action'
const myReducer = combineReducers({
sharedFooBar: networkAction.reducer(['loadFoo', 'saveBar' ])
})
The stored value will be:
// If still loading:
{ isLoading: true }
// On success:
{ isSuccess: true, value: action.payload }
// On error:
{ isError: true, error: action.payload }
In case you want to store certain extra info after dispatching the action, you could do it by adding the meta
object containing the info, just like in the example below:
import networkAction from '@gluedigital/network-action'
export const getImageById = (imageId) => networkAction({
action: 'getImageById',
url: 'http://localhost:3000/images',
body: { imageId },
meta: { imageId }
})
const fooApiActionReducer = combineReducers({
image: networkAction.reducer('getImageById'),
})
The stored value would include the meta
field with the original info provided to the action:
// If still loading:
{ isLoading: true, meta: { imageId: imageId } }
// On success:
{ isSuccess: true, value: action.payload, meta: { imageId: imageId } }
// On error:
{ isError: true, error: action.payload, meta: { imageId: imageId} }
There is another variant available at networkAction.keyedReducer
which saves a response similar to the previous one, but for each different value of the key attribute. The key can be either an attribute name that matches a field on the meta
object, or a function which returns a key derived from the whole action.
const myReducer = networkAction.keyedReducer('getTask', 'id')
const getTask = id => networkAction({
action: 'getTask',
url: 'http://localhost:3000/tasks/' + id,
meta: { id }
}))
// Usage demo:
await dispatch(getTask(1))
await dispatch(getTask(2))
console.log(getState())
// {
// 1: { isSuccess: true, value: ... },
// 2: { isError: true, error: ... },
// }