Redux middleware handling custom action in order to do XHR request with superagent (Tested with superagent@2.3.0)
You must create an instance of the middleware. It allows you to specific a configuration. Then append the instantiated middleware to redux's applyMiddleware :
import superagentMiddleware from "redux-superagent-middleware"
const superagentMiddlewareInstance = superagentMiddleware({
base : "http://amazing.api",
defaultHeaders : {
["Content-type"] : "application/json"
},
hooks : {
onRequest: (store, action, request) => { ... },
onFailure: (store, action, request, response) => { ... },
onError: (store, action, request, response) => { ... }
}
})
const store = createStore(
rootReducer,
compose(
applyMiddleware(superagentMiddlewareInstance, thunkMiddleware, routerMiddleware(browserHistory)),
window.devToolsExtension && !__PROD__ ? window.devToolsExtension() : f => f
)
)
Then you can create and dispatch an appropriate action :
dispatch({
request : {
url : "/v1/products"
},
onStart,
onSuccess,
onError,
onComplete,
})
Allow you to define the base of url (without pathname, parameters, ...). Example : http://api.com
Request's headers used in each request. Example :
const superagentMiddlewareInstance = superagentMiddleware({
defaultHeaders : {
["Content-type"] : "application/json"
},
...
})
Request's query params used in each request. Example :
const superagentMiddlewareInstance = superagentMiddleware({
defaultParams : {
["api_key"] : "1234567890"
},
...
})
You can specify a function called before each request, it allows you to modify it before xhr call.
This function have the following parameters :
- store : redux store
- action : custom dispatched flux action
- request : the pre-request built by middleware before xhr call
Return the modified request to apply modifications
Example :
const superagentMiddlewareInstance = superagentMiddleware({
hooks : {
onRequest : (store, action, request) => {
const state = store.getState()
const token = state.session.token
if (action.setToken === true){
request.params["token"] = token
}
return request
}
}
})
You can specify a function called if the request failed (connection error).
This hook is trigger before action.onError, action.onComplete, hooks.onError and you can interrupt middleware process by returning false
This function have these following parameters :
- store : redux store
- action : custom dispatched flux action
- request : the request used for xhr call
- response : xhr call's response
Example :
const superagentMiddlewareInstance = superagentMiddleware({
hooks : {
onFailure : (store, action, request, response) => {
Logger.send("request_failed", "....")
}
}
})
You can specify a function called if the request failed (connection error).
This hook is trigger before action.onError, action.onComplete and you can interrupt middleware process by returning false
This function have these following parameters :
- store : redux store
- action : custom dispatched flux action
- request : the request used for xhr call
- response : xhr call's response
Example :
const superagentMiddlewareInstance = superagentMiddleware({
hooks : {
onFailure : (store, action, request, response) => {
if (response.status === 401){
this.store.dispatch(logout())
// Prevent onError / onComplete if they are specified in action
return false
}
}
}
})
Request object supports these parameters :
- base (optional) : Specify the base of url of this request (example : http://anotherapi.com), it overload middleware config's base
- url (mandatory) : Specify the pathname (example : /v3/test ) or the complete url (http://test.api/v4/test)
- method (default value : GET) : http verb (GET/POST/PUT/DELETE/PATCH ...)
- params : query
const customAction = {
request : {
base : "http://anotherapi.com",
url : "v1/user",
method : "POST",
body : {
email : "test@test.te",
password : "example"
}
},
........ others options
}
const customAction = {
request : {
base : "http://anotherapi.com",
url : "v1/user",
method : "DELETE",
params : {
user_id: 1337,
}
},
........ others options
}
You can add 4 listeners, these listeners is function which accept 4 parameters :
- payload [mixed] : response.body
- meta [object] : metadata of the response, including httpCode
- dispatch [function] : redux's dispatch function
- getState [function] : redux's getState function
Available listeners :
- onStart : call before a request is executed (after onRequest hook)
- onSuccess : call when the request is done with a statusCode/httpCode < 300
- onError : call when the request is done with a statusCode/httpCode >= 300
- onComplete : call when the request is done
The listener can return a flux action which is sent to the next middleware
const customAction = {
request : {
base : "http://anotherapi.com",
url : "v1/user",
method : "DELETE",
params : {
user_id: 1337,
}
},
onStart : () => {
return {
type : "REQUEST_PENDING"
}
},
onSuccess : (payload, meta, dispatch) => {
// dispatch is useful is the new action must pass throught all middleware
// Or if you need to dispatch more than one action
dispatch({
...
})
// And you can do one/severals dispatch, and then use return
return {
type : "REQUEST_SUCCESS",
payload
}
},
onError : () => {
// Do something if the httpCode if >= 300
},
onComplete : () => {
return {
type : "STOP_SPINNER"
}
}
}
You can do several requests in the same action, listeners like onStart or onSuccess are call only once, when the first request start for onStart, and when all request are complete for onSuccess, onError and onComplete
If you interrupt the process in a config.hook like onFailure by returning false, all requests are interrupt
In order to recognize the request's response in the success/error payload you can give a name for the request (otherwise it is request_${index} )
Usage :
const customAction = {
request : [ // You specify an array for request paramater
{ // FIRST SUB-REQUEST
name : "userList",
url : "/users/",
},
{ // SECOND SUB-REQUEST
name : "productList",
url : "product",
},
],
onSuccess : (payload) => {
console.log(payload.userList, payload.productList)
}
}