Skip to content

Commit

Permalink
fix(useAxios)!: reject promise on execute (#2485)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
FRSgit and antfu committed Apr 13, 2023
1 parent f54a3c4 commit edece1a
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 30 deletions.
48 changes: 34 additions & 14 deletions packages/integrations/useAxios/index.test.ts
Expand Up @@ -181,9 +181,24 @@ describe('useAxios', () => {

await then((result) => {
expect(result.data.value.id).toBe(1)
expect(isLoading.value).toBeFalsy()
expect(onRejected).toBeCalledTimes(0)
}, onRejected)
expect(isLoading.value).toBeFalsy()

expect(onRejected).toBeCalledTimes(0)
})

test('execute rejects on error', async () => {
const { isLoading, then, execute } = useAxios(config, instance)
expect(isLoading.value).toBeFalsy()
execute(`${path}/wrong-url`)
expect(isLoading.value).toBeTruthy()
const onResolved = vitest.fn()
const onRejected = vitest.fn()

await then(onResolved, onRejected)
expect(isLoading.value).toBeFalsy()
expect(onResolved).toBeCalledTimes(0)
expect(onRejected).toBeCalledTimes(1)
})

test('calling axios with config change(param/data etc.) only', async () => {
Expand Down Expand Up @@ -262,23 +277,27 @@ describe('useAxios', () => {
test('should abort when loading', async () => {
const { isLoading, isFinished, isAborted, execute, abort } = useAxios(url, config, options)
expect(isLoading.value).toBeFalsy()
execute('https://jsonplaceholder.typicode.com/todos/2').then((result) => {
expect((result.error.value as Error)?.message).toBe('aborted')
expect(isFinished.value).toBeTruthy()
expect(isLoading.value).toBeFalsy()
expect(isAborted.value).toBeTruthy()
})
let error: any
const promise = execute('https://jsonplaceholder.typicode.com/todos/2')
.catch((e) => {
error = e
})
abort('aborted')
await promise
expect(isAborted.value).toBeTruthy()
expect(isFinished.value).toBeTruthy()
expect(error).toBeDefined()
})

test('missing url', async () => {
// prevent stderr in jsdom xhr
console.error = vi.fn()
// @ts-expect-error mock undefined url
const { execute } = useAxios(undefined, config, options)
const { error } = await execute()
expect(error.value).toBeDefined()
let error: any
await execute()
.catch(e => error = e)
expect(error).toBeDefined()
})

test('should call onSuccess when success', async () => {
Expand All @@ -296,6 +315,7 @@ describe('useAxios', () => {
const { execute, error, isLoading, isFinished } = useAxios(url, config, { ...options, onError })
expect(isLoading.value).toBeFalsy()
await execute('https://jsonplaceholder.typicode.com/todos/2/3')
.catch(() => {})
expect(onError).toHaveBeenCalledWith(error.value)
expect(isFinished.value).toBeTruthy()
expect(isLoading.value).toBeFalsy()
Expand All @@ -321,9 +341,9 @@ describe('useAxios', () => {
}
const { data, execute } = useAxios<ResType>(url, config, { ...options, initialData, resetOnExecute: true })
expect(data.value).toEqual(initialData)
await execute()
await execute().catch(() => {})
expect(data.value).toEqual({ completed: false, id: 1, title: 'delectus aut autem', userId: 1 })
await execute('/todos/312')
await execute('/todos/312').catch(() => {})
expect(data.value).toEqual(initialData)
})

Expand All @@ -342,9 +362,9 @@ describe('useAxios', () => {
}
const { data, execute } = useAxios<ResType>(url, config, { ...options, initialData })
expect(data.value).toEqual(initialData)
await execute()
await execute().catch(() => {})
expect(data.value).toEqual({ completed: false, id: 1, title: 'delectus aut autem', userId: 1 })
await execute('/todos/312')
await execute('/todos/312').catch(() => {})
expect(data.value).toEqual({ completed: false, id: 1, title: 'delectus aut autem', userId: 1 })
})

Expand Down
37 changes: 21 additions & 16 deletions packages/integrations/useAxios/index.ts
Expand Up @@ -54,13 +54,13 @@ export interface StrictUseAxiosReturn<T, R, D> extends UseAxiosReturn<T, R, D> {
/**
* Manually call the axios request
*/
execute: (url?: string | AxiosRequestConfig<D>, config?: AxiosRequestConfig<D>) => PromiseLike<StrictUseAxiosReturn<T, R, D>>
execute: (url?: string | AxiosRequestConfig<D>, config?: AxiosRequestConfig<D>) => Promise<StrictUseAxiosReturn<T, R, D>>
}
export interface EasyUseAxiosReturn<T, R, D> extends UseAxiosReturn<T, R, D> {
/**
* Manually call the axios request
*/
execute: (url: string, config?: AxiosRequestConfig<D>) => PromiseLike<EasyUseAxiosReturn<T, R, D>>
execute: (url: string, config?: AxiosRequestConfig<D>) => Promise<EasyUseAxiosReturn<T, R, D>>
}
export interface UseAxiosOptions<T = any> {
/**
Expand Down Expand Up @@ -103,19 +103,19 @@ export interface UseAxiosOptions<T = any> {
}
type OverallUseAxiosReturn<T, R, D> = StrictUseAxiosReturn<T, R, D> | EasyUseAxiosReturn<T, R, D>

export function useAxios<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>, options?: UseAxiosOptions): StrictUseAxiosReturn<T, R, D> & PromiseLike<StrictUseAxiosReturn<T, R, D>>
export function useAxios<T = any, R = AxiosResponse<T>, D = any>(url: string, instance?: AxiosInstance, options?: UseAxiosOptions): StrictUseAxiosReturn<T, R, D> & PromiseLike<StrictUseAxiosReturn<T, R, D>>
export function useAxios<T = any, R = AxiosResponse<T>, D = any>(url: string, config: AxiosRequestConfig<D>, instance: AxiosInstance, options?: UseAxiosOptions): StrictUseAxiosReturn<T, R, D> & PromiseLike<StrictUseAxiosReturn<T, R, D>>
export function useAxios<T = any, R = AxiosResponse<T>, D = any>(config?: AxiosRequestConfig<D>): EasyUseAxiosReturn<T, R, D> & PromiseLike<EasyUseAxiosReturn<T, R, D>>
export function useAxios<T = any, R = AxiosResponse<T>, D = any>(instance?: AxiosInstance): EasyUseAxiosReturn<T, R, D> & PromiseLike<EasyUseAxiosReturn<T, R, D>>
export function useAxios<T = any, R = AxiosResponse<T>, D = any>(config?: AxiosRequestConfig<D>, instance?: AxiosInstance): EasyUseAxiosReturn<T, R, D> & PromiseLike<EasyUseAxiosReturn<T, R, D>>
export function useAxios<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>, options?: UseAxiosOptions): StrictUseAxiosReturn<T, R, D> & Promise<StrictUseAxiosReturn<T, R, D>>
export function useAxios<T = any, R = AxiosResponse<T>, D = any>(url: string, instance?: AxiosInstance, options?: UseAxiosOptions): StrictUseAxiosReturn<T, R, D> & Promise<StrictUseAxiosReturn<T, R, D>>
export function useAxios<T = any, R = AxiosResponse<T>, D = any>(url: string, config: AxiosRequestConfig<D>, instance: AxiosInstance, options?: UseAxiosOptions): StrictUseAxiosReturn<T, R, D> & Promise<StrictUseAxiosReturn<T, R, D>>
export function useAxios<T = any, R = AxiosResponse<T>, D = any>(config?: AxiosRequestConfig<D>): EasyUseAxiosReturn<T, R, D> & Promise<EasyUseAxiosReturn<T, R, D>>
export function useAxios<T = any, R = AxiosResponse<T>, D = any>(instance?: AxiosInstance): EasyUseAxiosReturn<T, R, D> & Promise<EasyUseAxiosReturn<T, R, D>>
export function useAxios<T = any, R = AxiosResponse<T>, D = any>(config?: AxiosRequestConfig<D>, instance?: AxiosInstance): EasyUseAxiosReturn<T, R, D> & Promise<EasyUseAxiosReturn<T, R, D>>

/**
* Wrapper for axios.
*
* @see https://vueuse.org/useAxios
*/
export function useAxios<T = any, R = AxiosResponse<T>, D = any>(...args: any[]): OverallUseAxiosReturn<T, R, D> & PromiseLike<OverallUseAxiosReturn<T, R, D>> {
export function useAxios<T = any, R = AxiosResponse<T>, D = any>(...args: any[]): OverallUseAxiosReturn<T, R, D> & Promise<OverallUseAxiosReturn<T, R, D>> {
const url: string | undefined = typeof args[0] === 'string' ? args[0] : undefined
const argsPlaceholder = isString(url) ? 1 : 0
let defaultConfig: AxiosRequestConfig<D> = {}
Expand Down Expand Up @@ -178,6 +178,7 @@ export function useAxios<T = any, R = AxiosResponse<T>, D = any>(...args: any[])
isLoading.value = false
isFinished.value = false
}

const loading = (loading: boolean) => {
isLoading.value = loading
isFinished.value = !loading
Expand All @@ -190,15 +191,19 @@ export function useAxios<T = any, R = AxiosResponse<T>, D = any>(...args: any[])
if (resetOnExecute)
data.value = initialData!
}

const waitUntilFinished = () =>
new Promise<OverallUseAxiosReturn<T, R, D>>((resolve, reject) => {
until(isFinished).toBe(true)
// eslint-disable-next-line @typescript-eslint/no-use-before-define
.then(() => resolve(result))
.catch(reject)
.then(() => error.value ? reject(error.value) : resolve(result))
})
const then: PromiseLike<OverallUseAxiosReturn<T, R, D>>['then'] = (onFulfilled, onRejected) =>
waitUntilFinished().then(onFulfilled, onRejected)

const promise = {
then: (...args) => waitUntilFinished().then(...args),
catch: (...args) => waitUntilFinished().catch(...args),
} as Promise<OverallUseAxiosReturn<T, R, D>>

const execute: OverallUseAxiosReturn<T, R, D>['execute'] = (executeUrl: string | AxiosRequestConfig<D> | undefined = url, config: AxiosRequestConfig<D> = {}) => {
error.value = undefined
const _url = typeof executeUrl === 'string'
Expand All @@ -208,7 +213,7 @@ export function useAxios<T = any, R = AxiosResponse<T>, D = any>(...args: any[])
if (_url === undefined) {
error.value = new AxiosError(AxiosError.ERR_INVALID_URL)
isFinished.value = true
return { then }
return promise
}
resetData()
abort()
Expand All @@ -228,7 +233,7 @@ export function useAxios<T = any, R = AxiosResponse<T>, D = any>(...args: any[])
options.onFinish?.()
loading(false)
})
return { then }
return promise
}

if (immediate && url)
Expand All @@ -249,6 +254,6 @@ export function useAxios<T = any, R = AxiosResponse<T>, D = any>(...args: any[])

return {
...result,
then,
...promise,
}
}

0 comments on commit edece1a

Please sign in to comment.