mirror of
https://github.com/simoneb/axios-hooks.git
synced 2025-12-08 21:25:56 +00:00
feature(cancellation): reset the loading state when outgoing call is cancelled
Resolves #372. New dispatch action is created to explicitly denote a cancel state update.
This commit is contained in:
parent
8688277d5e
commit
11de90ad85
59
src/index.js
59
src/index.js
@ -5,7 +5,8 @@ import { dequal as deepEqual } from 'dequal/lite'
|
||||
|
||||
const actions = {
|
||||
REQUEST_START: 'REQUEST_START',
|
||||
REQUEST_END: 'REQUEST_END'
|
||||
REQUEST_END: 'REQUEST_END',
|
||||
REQUEST_CANCELED: 'REQUEST_CANCELED'
|
||||
}
|
||||
|
||||
const DEFAULT_OPTIONS = {
|
||||
@ -150,13 +151,11 @@ export function makeUseAxios(configureOptions) {
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
...(action.error || action.isManualCancel
|
||||
? {}
|
||||
: { data: action.payload.data }),
|
||||
...(action.isManualCancel
|
||||
? {}
|
||||
: { [action.error ? 'error' : 'response']: action.payload })
|
||||
...(action.error ? {} : { data: action.payload.data }),
|
||||
[action.error ? 'error' : 'response']: action.payload
|
||||
}
|
||||
case actions.REQUEST_CANCELED:
|
||||
return { ...state, loading: false }
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,12 +174,7 @@ export function makeUseAxios(configureOptions) {
|
||||
return response
|
||||
}
|
||||
|
||||
async function executeRequest(
|
||||
config,
|
||||
dispatch,
|
||||
isMounted,
|
||||
isCancelledManually
|
||||
) {
|
||||
async function executeRequest(config, dispatch) {
|
||||
try {
|
||||
dispatch({ type: actions.REQUEST_START })
|
||||
|
||||
@ -195,26 +189,16 @@ export function makeUseAxios(configureOptions) {
|
||||
if (!StaticAxios.isCancel(err)) {
|
||||
dispatch({ type: actions.REQUEST_END, payload: err, error: true })
|
||||
} else {
|
||||
if (isMounted.current && isCancelledManually.current) {
|
||||
isCancelledManually.current = false
|
||||
dispatch({ type: actions.REQUEST_END, isManualCancel: true })
|
||||
}
|
||||
dispatch({ type: actions.REQUEST_CANCELED })
|
||||
}
|
||||
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function request(
|
||||
config,
|
||||
options,
|
||||
dispatch,
|
||||
isMounted,
|
||||
isCancelledManually
|
||||
) {
|
||||
async function request(config, options, dispatch) {
|
||||
return (
|
||||
tryGetFromCache(config, options, dispatch) ||
|
||||
executeRequest(config, dispatch, isMounted, isCancelledManually)
|
||||
executeRequest(config, dispatch)
|
||||
)
|
||||
}
|
||||
|
||||
@ -231,8 +215,6 @@ export function makeUseAxios(configureOptions) {
|
||||
useDeepCompareMemoize(_options)
|
||||
)
|
||||
|
||||
const isCancelledManually = React.useRef(false)
|
||||
const isMounted = React.useRef(false)
|
||||
const cancelSourceRef = React.useRef()
|
||||
|
||||
const [state, dispatch] = React.useReducer(
|
||||
@ -246,9 +228,6 @@ export function makeUseAxios(configureOptions) {
|
||||
|
||||
const cancelOutstandingRequest = React.useCallback(manualCancel => {
|
||||
if (cancelSourceRef.current) {
|
||||
if (manualCancel) {
|
||||
isCancelledManually.current = true
|
||||
}
|
||||
cancelSourceRef.current.cancel()
|
||||
}
|
||||
}, [])
|
||||
@ -270,22 +249,10 @@ export function makeUseAxios(configureOptions) {
|
||||
},
|
||||
[cancelOutstandingRequest]
|
||||
)
|
||||
React.useEffect(() => {
|
||||
isMounted.current = true
|
||||
return () => {
|
||||
isMounted.current = false
|
||||
}
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!options.manual) {
|
||||
request(
|
||||
withCancelToken(config),
|
||||
options,
|
||||
dispatch,
|
||||
isMounted,
|
||||
isCancelledManually
|
||||
).catch(() => {})
|
||||
request(withCancelToken(config), options, dispatch).catch(() => {})
|
||||
}
|
||||
|
||||
return () => cancelOutstandingRequest(options.manual)
|
||||
@ -301,9 +268,7 @@ export function makeUseAxios(configureOptions) {
|
||||
...(isReactEvent(configOverride) ? null : configOverride)
|
||||
}),
|
||||
{ useCache: false, ...options },
|
||||
dispatch,
|
||||
isMounted,
|
||||
isCancelledManually
|
||||
dispatch
|
||||
)
|
||||
},
|
||||
[config, withCancelToken]
|
||||
|
||||
@ -489,28 +489,6 @@ function standardTests(
|
||||
expect(result.current[0].loading).toBe(false)
|
||||
})
|
||||
|
||||
it('should not dispatch an error when the request is canceled', async () => {
|
||||
const cancellation = new Error('canceled')
|
||||
|
||||
axios.mockRejectedValueOnce(cancellation)
|
||||
axios.isCancel = jest
|
||||
.fn()
|
||||
.mockImplementationOnce(err => err === cancellation)
|
||||
|
||||
const { result, waitFor } = setup('')
|
||||
|
||||
// if we cancel we won't dispatch the error, hence there's no state update
|
||||
// to wait for. yet, if we don't try to wait, we won't know if we're handling
|
||||
// the error properly because the return value will not have the error until a
|
||||
// state update happens. it would be great to have a better way to test this
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(result.current[0].error).toBeNull()
|
||||
},
|
||||
{ timeout: 1000, suppressErrors: false }
|
||||
)
|
||||
})
|
||||
|
||||
it('should return previous state after cancel', async () => {
|
||||
const response = { data: 'whatever' }
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user