Skip to content

Commit 98a83d7

Browse files
tolkingantfuautofix-ci[bot]
authored
feat(useFetch): add parameters to the afterFetch and onFetchError (#4499)
Co-authored-by: Anthony Fu <[email protected]> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 01acd57 commit 98a83d7

File tree

3 files changed

+130
-1
lines changed

3 files changed

+130
-1
lines changed

packages/core/useFetch/index.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,82 @@ const { isFetching, error, data } = useMyFetch('users', {
212212
})
213213
```
214214

215+
You can re-execute the request by calling the `execute` method in `afterFetch` or `onFetchError`. Here is a simple example of refreshing a token:
216+
217+
```ts
218+
let isRefreshing = false
219+
const refreshSubscribers: Array<() => void> = []
220+
221+
const useMyFetch = createFetch({
222+
baseUrl: 'https://my-api.com',
223+
options: {
224+
async beforeFetch({ options }) {
225+
const myToken = await getMyToken()
226+
options.headers.Authorization = `Bearer ${myToken}`
227+
228+
return { options }
229+
},
230+
afterFetch({ data, response, context, execute }) {
231+
if (needRefreshToken) {
232+
if (!isRefreshing) {
233+
isRefreshing = true
234+
refreshToken().then((newToken) => {
235+
if (newToken.value) {
236+
isRefreshing = false
237+
setMyToken(newToken.value)
238+
onRrefreshed()
239+
}
240+
else {
241+
refreshSubscribers.length = 0
242+
// handle refresh token error
243+
}
244+
})
245+
}
246+
247+
return new Promise((resolve) => {
248+
addRefreshSubscriber(() => {
249+
execute().then((response) => {
250+
resolve({ data, response })
251+
})
252+
})
253+
})
254+
}
255+
256+
return { data, response }
257+
},
258+
// or use onFetchError with updateDataOnError
259+
updateDataOnError: true,
260+
onFetchError({ error, data, response, context, execute }) {
261+
// same as afterFetch
262+
return { error, data }
263+
},
264+
},
265+
fetchOptions: {
266+
mode: 'cors',
267+
},
268+
})
269+
270+
async function refreshToken() {
271+
const { data, execute } = useFetch<string>('refresh-token', {
272+
immediate: false,
273+
})
274+
275+
await execute()
276+
return data
277+
}
278+
279+
function onRrefreshed() {
280+
refreshSubscribers.forEach(callback => callback())
281+
refreshSubscribers.length = 0
282+
}
283+
284+
function addRefreshSubscriber(callback: () => void) {
285+
refreshSubscribers.push(callback)
286+
}
287+
288+
const { isFetching, error, data } = useMyFetch('users')
289+
```
290+
215291
### Events
216292

217293
The `onFetchResponse` and `onFetchError` will fire on fetch request responses and errors respectively.

packages/core/useFetch/index.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { AfterFetchContext, OnFetchErrorContext } from '.'
12
import { until } from '@vueuse/shared'
23
import { beforeEach, describe, expect, it, vi } from 'vitest'
34
import { nextTick, ref } from 'vue'
@@ -768,4 +769,42 @@ describe.skipIf(isBelowNode18)('useFetch', () => {
768769
expect(isFetching.value).toBe(false)
769770
expect(isFinished.value).toBe(true)
770771
})
772+
773+
it('should be possible to re-trigger the request via the afterFetch parameters', async () => {
774+
let count = 0
775+
let options: Partial<AfterFetchContext> = {}
776+
useFetch('https://example.com', {
777+
afterFetch: (ctx) => {
778+
!count && ctx.execute()
779+
count++
780+
options = ctx
781+
return ctx
782+
},
783+
})
784+
785+
await vi.waitFor(() => {
786+
expect(fetchSpy).toHaveBeenCalledTimes(2)
787+
expect(options?.context).toBeDefined()
788+
expect(options?.execute).toBeDefined()
789+
})
790+
})
791+
792+
it('should be possible to re-trigger the request via the onFetchError parameters', async () => {
793+
let count = 0
794+
let options: Partial<OnFetchErrorContext> = {}
795+
useFetch('https://example.com?status=400&json', {
796+
onFetchError: (ctx) => {
797+
!count && ctx.execute()
798+
count++
799+
options = ctx
800+
return ctx
801+
},
802+
})
803+
804+
await vi.waitFor(() => {
805+
expect(fetchSpy).toHaveBeenCalledTimes(2)
806+
expect(options?.context).toBeDefined()
807+
expect(options?.execute).toBeDefined()
808+
})
809+
})
771810
})

packages/core/useFetch/index.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,22 @@ export interface AfterFetchContext<T = any> {
118118
response: Response
119119

120120
data: T | null
121+
122+
context: BeforeFetchContext
123+
124+
execute: (throwOnFailed?: boolean) => Promise<any>
121125
}
122126

123127
export interface OnFetchErrorContext<T = any, E = any> {
124128
error: E
125129

126130
data: T | null
131+
132+
response: Response | null
133+
134+
context: BeforeFetchContext
135+
136+
execute: (throwOnFailed?: boolean) => Promise<any>
127137
}
128138

129139
export interface UseFetchOptions {
@@ -185,7 +195,7 @@ export interface UseFetchOptions {
185195
* Will run immediately after the fetch request is returned.
186196
* Runs after any 4xx and 5xx response
187197
*/
188-
onFetchError?: (ctx: { data: any, response: Response | null, error: any }) => Promise<Partial<OnFetchErrorContext>> | Partial<OnFetchErrorContext>
198+
onFetchError?: (ctx: OnFetchErrorContext) => Promise<Partial<OnFetchErrorContext>> | Partial<OnFetchErrorContext>
189199
}
190200

191201
export interface CreateFetchOptions {
@@ -483,6 +493,8 @@ export function useFetch<T>(url: MaybeRefOrGetter<string>, ...args: any[]): UseF
483493
({ data: responseData } = await options.afterFetch({
484494
data: responseData,
485495
response: fetchResponse,
496+
context,
497+
execute,
486498
}))
487499
}
488500
data.value = responseData
@@ -498,6 +510,8 @@ export function useFetch<T>(url: MaybeRefOrGetter<string>, ...args: any[]): UseF
498510
data: responseData,
499511
error: fetchError,
500512
response: response.value,
513+
context,
514+
execute,
501515
}))
502516
}
503517

0 commit comments

Comments
 (0)