Skip to content

Commit a61c35c

Browse files
authored
Merge pull request #157 from danybeltran/feat-custom-fetcher
Feat custom fetcher
2 parents d7efad0 + 5c0b273 commit a61c35c

File tree

6 files changed

+118
-77
lines changed

6 files changed

+118
-77
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@ With one hook call, you get all the information about a request that you can use
1616
```jsx
1717
import useFetch from "http-react"
1818

19+
// This is the default
20+
const fetcher = (url, config) => fetch(url, config)
21+
1922
export default function App() {
2023
const { data, loading, error, responseTime } = useFetch("/api/user-info", {
21-
refresh: '30 sec'
24+
refresh: '30 sec',
25+
fetcher
2226
})
2327

2428
if (loading) return <p>Loading</p>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "http-react",
3-
"version": "2.9.9",
3+
"version": "3.0.0",
44
"description": "React hooks for data fetching",
55
"main": "dist/index.js",
66
"scripts": {

src/hooks/use-fetch.ts

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -117,17 +117,19 @@ export function useFetch<FetchDataType = any, BodyType = any>(
117117
onResolve,
118118
onAbort,
119119
refresh = isDefined(ctx.refresh) ? ctx.refresh : 0,
120-
cancelOnChange = true,
121120
attempts = ctx.attempts,
122121
attemptInterval = ctx.attemptInterval,
123122
revalidateOnFocus = ctx.revalidateOnFocus,
124123
suspense: $suspense,
125124
onFetchStart = ctx.onFetchStart,
126125
onFetchEnd = ctx.onFetchEnd,
127126
cacheIfError = ctx.cacheIfError,
128-
maxCacheAge = ctx.maxCacheAge
127+
maxCacheAge = ctx.maxCacheAge,
128+
fetcher = ctx.fetcher
129129
} = optionsConfig
130130

131+
const $fetch = isFunction(fetcher) ? fetcher : fetch
132+
131133
const config = {
132134
query,
133135
params,
@@ -535,44 +537,50 @@ export function useFetch<FetchDataType = any, BodyType = any>(
535537
cacheProvider.set('requestStart' + resolvedDataKey, Date.now())
536538
requestInitialTimes[resolvedDataKey] = Date.now()
537539

538-
const r = isRequest
539-
? new Request(realUrl + c.query, {
540-
...ctx,
541-
...reqConfig,
542-
...optionsConfig,
543-
signal: (() => {
544-
return newAbortController.signal
545-
})(),
546-
headers: {
547-
'Content-Type': 'application/json',
548-
...ctx.headers,
549-
..._headers,
550-
...config.headers,
551-
...c.headers
540+
const newRequestConfig = (
541+
isRequest
542+
? {
543+
...ctx,
544+
...reqConfig,
545+
...optionsConfig,
546+
signal: (() => {
547+
return newAbortController.signal
548+
})(),
549+
headers: {
550+
'Content-Type': 'application/json',
551+
...ctx.headers,
552+
..._headers,
553+
...config.headers,
554+
...c.headers
555+
}
552556
}
553-
} as any)
554-
: new Request(realUrl + c.query, {
555-
...ctx,
556-
...optionsConfig,
557-
signal: (() => {
558-
return newAbortController.signal
559-
})(),
560-
body: isFunction(formatBody)
561-
? // @ts-ignore // If formatBody is a function
562-
formatBody(optionsConfig?.body as any)
563-
: optionsConfig?.body,
564-
headers: {
565-
'Content-Type': 'application/json',
566-
...ctx.headers,
567-
...config.headers,
568-
...c.headers
557+
: {
558+
...ctx,
559+
...optionsConfig,
560+
signal: (() => {
561+
return newAbortController.signal
562+
})(),
563+
body: isFunction(formatBody)
564+
? // @ts-ignore // If formatBody is a function
565+
formatBody(optionsConfig?.body as any)
566+
: optionsConfig?.body,
567+
headers: {
568+
'Content-Type': 'application/json',
569+
...ctx.headers,
570+
...config.headers,
571+
...c.headers
572+
}
569573
}
570-
})
574+
) as any
575+
576+
const r = new Request(realUrl + c.query, newRequestConfig)
577+
571578
if (logStart) {
572579
;(onFetchStart as any)(r, optionsConfig, ctx)
573580
}
574581

575-
const json = await fetch(r)
582+
// @ts-ignore - This could use 'axios' or something similar
583+
const json = await $fetch(r.url, newRequestConfig)
576584

577585
const resolvedDate = Date.now()
578586

@@ -587,7 +595,7 @@ export function useFetch<FetchDataType = any, BodyType = any>(
587595

588596
lastResponses[resolvedKey] = json
589597

590-
const code = json.status
598+
const code = json.status as number
591599
statusCodes[resolvedKey] = code
592600

593601
$$error = false
@@ -599,7 +607,8 @@ export function useFetch<FetchDataType = any, BodyType = any>(
599607
code
600608
}
601609

602-
const _data = await (resolver as any)(json)
610+
// @ts-ignore - 'data' is priority because 'fetcher' can return it
611+
const _data = json?.['data'] ?? (await (resolver as any)(json))
603612
if (code >= 200 && code < 400) {
604613
hasData[resolvedDataKey] = true
605614
hasData[resolvedKey] = true

src/types/index.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ export type HTTP_METHODS =
1111
| 'UNLINK'
1212

1313
export type FetchContextType = {
14+
fetcher?(
15+
url: string,
16+
config: FetchConfigType
17+
): Promise<{
18+
json?: any
19+
data?: any
20+
status?: number
21+
blob?: any
22+
text?: any
23+
}>
1424
headers?: any
1525
baseUrl?: string
1626
/**
@@ -78,8 +88,7 @@ export type RequestWithBody = <R = any, BodyType = any>(
7888
/**
7989
* The request configuration
8090
*/
81-
reqConfig?: Omit<RequestInit, 'body'> & {
82-
body?: BodyType
91+
reqConfig?: Omit<RequestInit & FetchConfigType<R, BodyType>, 'suspense'> & {
8392
/**
8493
* Default value
8594
*/
@@ -91,7 +100,7 @@ export type RequestWithBody = <R = any, BodyType = any>(
91100
/**
92101
* The function that formats the body
93102
*/
94-
formatBody?(b: BodyType): any
103+
formatBody?: any
95104
/**
96105
* Request params (like Express)
97106
*/
@@ -114,7 +123,7 @@ export type RequestWithBody = <R = any, BodyType = any>(
114123
error: any
115124
data: R
116125
config: RequestInit
117-
code: number
126+
status: number
118127
res: CustomResponse<R>
119128
}>
120129

@@ -136,12 +145,24 @@ export type ImperativeFetch = {
136145
purge: RequestWithBody
137146
link: RequestWithBody
138147
unlink: RequestWithBody
148+
config?: FetchContextType & FetchInit
139149
}
140150

141151
export type FetchConfigType<FetchDataType = any, BodyType = any> = Omit<
142152
RequestInit,
143-
'body'
153+
'body' | 'headers'
144154
> & {
155+
headers?: any
156+
fetcher?(
157+
url: string,
158+
config: FetchConfigType<FetchDataType, BodyType>
159+
): Promise<{
160+
json?: any
161+
data?: any
162+
status?: number
163+
blob?: any
164+
text?: any
165+
}>
145166
body?: BodyType
146167
/**
147168
* Any serializable id. This is optional.

src/utils/index.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -62,21 +62,24 @@ export const createImperativeFetch = (ctx: FetchContextType) => {
6262

6363
const { baseUrl } = ctx
6464

65-
return Object.fromEntries(
66-
new Map(
67-
keys.map(k => [
68-
k.toLowerCase(),
69-
(url, config = {}) =>
70-
(useFetch as any)[k.toLowerCase()](
71-
hasBaseUrl(url) ? url : baseUrl + url,
72-
{
73-
...ctx,
74-
...config
75-
}
76-
)
77-
])
78-
)
79-
) as ImperativeFetch
65+
return {
66+
...Object.fromEntries(
67+
new Map(
68+
keys.map(k => [
69+
k.toLowerCase(),
70+
(url, config = {}) =>
71+
(useFetch as any)[k.toLowerCase()](
72+
hasBaseUrl(url) ? url : baseUrl + url,
73+
{
74+
...ctx,
75+
...config
76+
}
77+
)
78+
])
79+
)
80+
),
81+
config: ctx
82+
} as ImperativeFetch
8083
}
8184

8285
/**

src/utils/shared.ts

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ export function createRequestFn(
121121
headers,
122122
query = {},
123123
body,
124+
method: $method = method,
124125
formatBody,
125126
resolver = DEFAULT_RESOLVER,
126127
onResolve = () => {},
@@ -134,7 +135,7 @@ export function createRequestFn(
134135
.join('&')
135136

136137
const reqConfig = {
137-
method,
138+
method: $method,
138139
headers: {
139140
'Content-Type': 'application/json',
140141
...$headers,
@@ -170,7 +171,7 @@ export function createRequestFn(
170171
res: req,
171172
data: def,
172173
error: true,
173-
code: req?.status,
174+
status: req?.status,
174175
config: {
175176
...init,
176177
url: `${baseUrl || ''}${rawUrl}`,
@@ -184,7 +185,7 @@ export function createRequestFn(
184185
res: req,
185186
data: data,
186187
error: false,
187-
code: req?.status,
188+
status: req?.status,
188189
config: {
189190
...init,
190191
url: `${baseUrl || ''}${rawUrl}`,
@@ -199,7 +200,7 @@ export function createRequestFn(
199200
res: r,
200201
data: def,
201202
error: true,
202-
code: r?.status,
203+
status: r?.status,
203204
config: {
204205
...init,
205206
url: requestUrl,
@@ -226,21 +227,24 @@ const createImperativeFetch = (ctx: FetchContextType) => {
226227

227228
const { baseUrl } = ctx
228229

229-
return Object.fromEntries(
230-
new Map(
231-
keys.map(k => [
232-
k.toLowerCase(),
233-
(url, config = {}) =>
234-
(Client as any)[k.toLowerCase()](
235-
hasBaseUrl(url) ? url : baseUrl + url,
236-
{
237-
...ctx,
238-
...config
239-
}
240-
)
241-
])
242-
)
243-
) as ImperativeFetch
230+
return {
231+
...Object.fromEntries(
232+
new Map(
233+
keys.map(k => [
234+
k.toLowerCase(),
235+
(url: string, config = {}) =>
236+
(Client as any)[k.toLowerCase()](
237+
hasBaseUrl(url) ? url : baseUrl + url,
238+
{
239+
...ctx,
240+
...config
241+
}
242+
)
243+
])
244+
)
245+
),
246+
config: ctx
247+
} as ImperativeFetch
244248
}
245249

246250
/**

0 commit comments

Comments
 (0)