saturate v1.2.0
Saturate
Spooning redux-api-middleware and adding extra functionality for the MyLotto 2.0 CWP project.
Redux middleware for calling an API.
Usage
Defining the API call
The parameters of the API call are specified by root properties of the [CALL_API]
property of an RSAA.
[CALL_API].endpoint
The URL endpoint for the API call.
It is usually a string, be it a plain old one or an ES2015 template string. It may also be a function taking the state of your Redux store as its argument, and returning such a string.
[CALL_API].method
The HTTP method for the API call.
It must be one of the strings GET
, HEAD
, POST
, PUT
, PATCH
, DELETE
or OPTIONS
, in any mixture of lowercase and uppercase letters.
[CALL_API].body
The body of the API call.
redux-api-middleware
uses isomorphic-fetch
to make the API call. [CALL_API].body
should hence be a valid body according to the the fetch specification. In most cases, this will be a JSON-encoded string or a FormData
object.
[CALL_API].headers
The HTTP headers for the API call.
It is usually an object, with the keys specifying the header names and the values containing their content. For example, you can let the server know your call contains a JSON-encoded string body in the following way.
{
[CALL_API]: {
...
headers: { 'Content-Type': 'application/json' }
...
}
}
It may also be a function taking the state of your Redux store as its argument, and returning an object of headers as above.
[CALL_API].credentials
Whether or not to send cookies with the API call.
It must be one of the following strings:
omit
is the default, and does not send any cookies;same-origin
only sends cookies for the current domain;include
always send cookies, even for cross-origin calls.
[CALL_API].bailout
Whether or not to make the API call.
It must be one of the following:
boolean
true or false, true will not make the API call;function
returning true or false, true will not make the API call.
In some cases, the data you would like to fetch from the server may already be cached in you Redux store. Or you may decide that the current user does not have the necessary permissions to make some request.
You can tell redux-api-middleware
to not make the API call through [CALL_API].bailout
. If the value is true
, the RSAA will die here, and no FSA will be passed on to the next middleware.
A more useful possibility is to give [CALL_API].bailout
a function. At runtime, it will be passed the state of your Redux store as its only argument, if the return value of the function is true
, the API call will not be made.
Example Usage in MyLotto CWP
Action
import { CALL_API } from 'saturate';
export const REQUEST = 'GET_HOME_REQUEST'
export const SUCCESS = 'GET_HOME_SUCCESS'
export const FAILURE = 'GET_HOME_FAILURE'
export function getHome() {
let endpoint = `http://${process.env.CMS_HOST}:8080/content?page=/`
return {
// any action not prefixed by [CALL_API] will not get touched by the middleware
[CALL_API]: {
// the API endpoint
endpoint,
// the HTTP request method
method: 'GET',
// the action types to fire
types: [REQUEST, SUCCESS, FAILURE],
// bailout function that makes sure the request goes through (optional)
bailout: function() {
// return true if the API call should not be made
return false
}
}
}
}
Reducer
import {REQUEST, SUCCESS, FAILURE} from '../actions'
const homeInitialState = {
isFetching: false,
didInvalidate: false,
content: {}
}
export default function home (state = homeInitialState, action) {
switch (action.type) {
// return a loading state
case REQUEST:
return Object.assign({}, state, {
isFetching: true,
didInvalidate: false
})
// request was successful, return the data
case SUCCESS:
return Object.assign({}, state, {
isFetching: false,
didInvalidate: true,
contents: action.payload.data
})
// request failed, show some error message
case FAILURE:
return Object.assign({}, state, {
isFetching: false,
didInvalidate: true,
error: action.payload.message
})
// return the initial state
default:
return state
}
}
Container
import React, {Component} from 'react'
import {connect} from 'react-redux'
import * as HomeActions from '../actions'
import HomeBody from '../components/home-body'
@connect(state => ({home: state.home.contents}))
export default class Home extends Component {
// gets evaluated by saturate, any methods imported and inserted in to the needs array will get run in a non-specific order
static needs = [
HomeActions.getHome
];
render () {
var {data} = this.props
return (
<div>{data}</div>
)
}
}
TODO
add bailout promise