Created
December 27, 2017 11:27
-
-
Save alexeychikk/2078366d8fc975922294ae6210648a0f to your computer and use it in GitHub Desktop.
Cancellation middleware for redux-axios-middleware
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
CANCEL_ACTION_REQUESTS, | |
CANCEL_ALL_ACTION_REQUESTS | |
} from './constants'; | |
export function cancelActionRequest(actionType) { | |
return { type: CANCEL_ACTION_REQUESTS, actionType }; | |
} | |
export function cancelAllActionRequests() { | |
return { type: CANCEL_ALL_ACTION_REQUESTS }; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const CANCEL_ACTION_REQUESTS = 'CANCEL_ACTION_REQUESTS'; | |
export const CANCEL_ALL_ACTION_REQUESTS = 'CANCEL_ALL_ACTION_REQUESTS'; | |
export const CANCEL_DATA = { cancelled: true }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export { createCancellationMiddleware } from './middleware'; | |
export * from './actions'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import axios from 'axios'; | |
import { | |
CANCEL_ACTION_REQUESTS, | |
CANCEL_ALL_ACTION_REQUESTS, | |
CANCEL_DATA | |
} from './constants'; | |
const cancelRequest = token => { | |
token.cancel(CANCEL_DATA); | |
token.onCancelCallback && token.onCancelCallback(); | |
}; | |
export function createCancellationMiddleware() { | |
return store => { | |
let tokensMap = {}; | |
return next => action => { | |
const { actionType, payload, type, types } = action; | |
if (payload && payload.request && types && types.length && !type) { | |
const [actionTypeRequest] = types; | |
const source = axios.CancelToken.source(); | |
source.onCancelCallback = payload.request.onCancel; | |
// Extend action so that redux-axios-middleware | |
// can understand that request is cancellable. | |
const cancelableAction = { | |
...action, | |
payload: { | |
...payload, | |
request: { | |
...payload.request, | |
cancelToken: source.token | |
} | |
} | |
}; | |
// Store cancellation token so that we can cancel it | |
// later using actions.js in this folder. | |
const actionTokens = tokensMap[actionTypeRequest] || []; | |
actionTokens.push(source); | |
tokensMap[actionTypeRequest] = actionTokens; | |
return next(cancelableAction); | |
} | |
// Listen for cancel actions and cancel requests appropriately. | |
if (type === CANCEL_ACTION_REQUESTS) { | |
const actionTypes = Array.isArray(actionType) | |
? actionType | |
: [actionType]; | |
actionTypes.forEach(actionType => { | |
const actionTokens = tokensMap[actionType]; | |
if (actionTokens) { | |
actionTokens.forEach(cancelRequest); | |
tokensMap[actionType] = []; | |
} | |
}); | |
} else if (type === CANCEL_ALL_ACTION_REQUESTS) { | |
Object.values(tokensMap).forEach(actionTokens => | |
actionTokens.forEach(cancelRequest) | |
); | |
tokensMap = {}; | |
} | |
return next(action); | |
}; | |
}; | |
} |
Hi, how do we use this in components ?
const myAction = () => ({ type: constants.MY_ACTION });
const cancelMyAction = () => cancelActionRequest(constants.MY_ACTION);
Then in your component bind cancelMyAction
through react-redux
.
This code is not properly tested so be careful.
Fresh, working version with TypeScript
import { AnyAction, Middleware } from 'redux'
import axios, { CancelTokenSource } from 'axios'
interface LooseObject {
[key: string]: CancelTokenSource[]
}
const CANCEL_ACTION_REQUESTS = 'CANCEL_ACTION_REQUESTS'
const CANCEL_ALL_ACTION_REQUESTS = 'CANCEL_ALL_ACTION_REQUESTS'
export const cancelAllActionRequests = (): AnyAction => {
return { type: CANCEL_ALL_ACTION_REQUESTS }
}
export const cancelActionRequest = (actionType: string | string[]): AnyAction => {
return { type: CANCEL_ACTION_REQUESTS, actionType }
}
const cancelRequest = (token: CancelTokenSource) => {
token.cancel()
}
export const cancelAxiosRequestMiddleware: Middleware = () => {
let tokensMap: LooseObject = {}
return (next) => async (action) => {
const { type, payload, actionType } = action
if (payload && payload.request) {
const source = axios.CancelToken.source()
const cancelableAction = {
...action,
payload: {
...payload,
request: {
...payload.request,
cancelToken: source.token,
},
},
}
const actionTokens = tokensMap[type] || []
actionTokens.push(source)
tokensMap[type] = actionTokens
return next(cancelableAction)
}
if (type === CANCEL_ACTION_REQUESTS) {
const actionTypes = Array.isArray(actionType) ? actionType : [actionType]
actionTypes.forEach((at) => {
const actionTokens = tokensMap[at]
if (actionTokens) {
actionTokens.forEach(cancelRequest)
tokensMap[at] = []
}
})
}
if (type === CANCEL_ALL_ACTION_REQUESTS) {
Object.values(tokensMap).forEach((actionTokens) => actionTokens.forEach(cancelRequest))
tokensMap = {}
}
return next(action)
}
}
Usage:
dispatch(cancelAllActionRequests())
// OR
dispatch(cancelActionRequest('YOUR_ACTION_TYPE'))
//OR
dispatch(cancelActionRequest(['YOUR_ACTION_TYPE', 'YOUR_ACTION_TYPE_N']))
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, how do we use this in components ?