Skip to content

Commit

Permalink
feats(Suspense):
Browse files Browse the repository at this point in the history
Improves suspense usage
  • Loading branch information
danybeltran committed Dec 7, 2024
1 parent 90eeeb7 commit ab4c876
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 102 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "http-react",
"version": "3.6.92",
"version": "3.7.0",
"description": "React hooks for data fetching",
"main": "dist/index.js",
"scripts": {
Expand Down
218 changes: 117 additions & 101 deletions src/hooks/use-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,13 @@ export function useFetch<FetchDataType = any, BodyType = any>(
...options
}
: isRequest
? {
url: init.url,
method: init.method,
init,
...options
}
: (init as FetchConfigType<FetchDataType, BodyType>)
? {
url: init.url,
method: init.method,
init,
...options
}
: (init as FetchConfigType<FetchDataType, BodyType>)

const {
onOnline = ctx.onOnline,
Expand Down Expand Up @@ -162,7 +162,11 @@ export function useFetch<FetchDataType = any, BodyType = any>(
middleware = ctx.middleware
} = optionsConfig

const $fetch = isFunction(fetcher) ? fetcher : fetch
const $fetch = isFunction(fetcher)
? fetcher
: windowExists
? fetch
: () => new Response(serialize(initialDataValue))

const config = {
query,
Expand Down Expand Up @@ -194,8 +198,8 @@ export function useFetch<FetchDataType = any, BodyType = any>(
optionsConfig.auto === false
? false
: isDefined(optionsConfig.retryOnReconnect)
? optionsConfig.retryOnReconnect
: ctx.retryOnReconnect
? optionsConfig.retryOnReconnect
: ctx.retryOnReconnect

const reqQuery = {
...ctx.query,
Expand All @@ -211,10 +215,10 @@ export function useFetch<FetchDataType = any, BodyType = any>(
(hasBaseUrl(url)
? ''
: !isDefined(config.baseUrl)
? !isDefined(ctx.baseUrl)
? ''
: ctx.baseUrl
: config.baseUrl) + url
? !isDefined(ctx.baseUrl)
? ''
: ctx.baseUrl
: config.baseUrl) + url

const defaultId = [method, url].join(' ')

Expand Down Expand Up @@ -351,6 +355,8 @@ export function useFetch<FetchDataType = any, BodyType = any>(

const initialDataValue = valuesMemory.get(resolvedKey) ?? requestCache ?? def

const hasInitialOrFallbackData = isDefined(initialDataValue)

const [fetchState, setFetchState] = useState({
data: initialDataValue,
online: true,
Expand Down Expand Up @@ -517,10 +523,10 @@ export function useFetch<FetchDataType = any, BodyType = any>(
(hasBaseUrl(url)
? ''
: !isDefined(config.baseUrl)
? !isDefined(ctx.baseUrl)
? ''
: ctx.baseUrl
: config.baseUrl) + url
? !isDefined(ctx.baseUrl)
? ''
: ctx.baseUrl
: config.baseUrl) + url

const urlWithParams = setURLParams(rawUrl, c.params)

Expand Down Expand Up @@ -634,8 +640,8 @@ export function useFetch<FetchDataType = any, BodyType = any>(
(realUrl.includes('?')
? c.query
: c.query
? '?' + c.query
: c.query)
? '?' + c.query
: c.query)
).replace('?&', '?'),
newRequestConfig
)
Expand Down Expand Up @@ -1225,51 +1231,48 @@ export function useFetch<FetchDataType = any, BodyType = any>(
// Attempts will be made after a request fails

// if ((attempts as number) > 0) {
const tm = setTimeout(
() => {
if (!gettingAttempts.get(resolvedKey)) {
gettingAttempts.set(resolvedKey, true)
const attempts =
typeof $attempts === 'function'
? $attempts({
status:
statusCodes.get(resolvedKey) ||
statusCodes.get(resolvedDataKey),
res: lastResponses.get(resolvedKey),
error:
hasErrors.get(resolvedKey) ||
hasErrors.get(resolvedDataKey) ||
(error as any),
completedAttempts
})
: $attempts

if ((attempts as number) > 0) {
if (completedAttempts < (attempts as number)) {
reValidate()
setCompletedAttempts((previousAttempts: number) => {
let newAttemptsValue = previousAttempts + 1
const tm = setTimeout(() => {
if (!gettingAttempts.get(resolvedKey)) {
gettingAttempts.set(resolvedKey, true)
const attempts =
typeof $attempts === 'function'
? $attempts({
status:
statusCodes.get(resolvedKey) ||
statusCodes.get(resolvedDataKey),
res: lastResponses.get(resolvedKey),
error:
hasErrors.get(resolvedKey) ||
hasErrors.get(resolvedDataKey) ||
(error as any),
completedAttempts
})
: $attempts

requestsProvider.emit(resolvedKey, {
requestCallId,
completedAttempts: newAttemptsValue
})
if ((attempts as number) > 0) {
if (completedAttempts < (attempts as number)) {
reValidate()
setCompletedAttempts((previousAttempts: number) => {
let newAttemptsValue = previousAttempts + 1

return newAttemptsValue
})
} else if (completedAttempts === attempts) {
requestsProvider.emit(resolvedKey, {
requestCallId,
online: false,
error: true
completedAttempts: newAttemptsValue
})
if (inDeps('online')) setOnline(false)
}

return newAttemptsValue
})
} else if (completedAttempts === attempts) {
requestsProvider.emit(resolvedKey, {
requestCallId,
online: false,
error: true
})
if (inDeps('online')) setOnline(false)
}
}
},
getMiliseconds(attemptInterval as TimeSpan)
)
}
}, getMiliseconds(attemptInterval as TimeSpan))
// }
return () => {
clearTimeout(tm)
Expand Down Expand Up @@ -1310,37 +1313,44 @@ export function useFetch<FetchDataType = any, BodyType = any>(
])

const initializeRevalidation = useCallback(
async function initializeRevalidation() {
let d = undefined
if (canRevalidate) {
if (url !== '') {
d = await fetchData({
query: Object.keys(reqQuery)
.map(q =>
Array.isArray(reqQuery[q])
? reqQuery[q]
.map((queryItem: any) => [q, queryItem].join('='))
.join('&')
: [q, reqQuery[q]].join('=')
)
.join('&'),
params: reqParams
})
} else {
d = def
// It means a url is not passed
setFetchState(prev => ({
...prev,
loading: false,
error: hasErrors.get(resolvedDataKey) || hasErrors.get(resolvedKey),
completedAttempts: prev.completedAttempts
}))
windowExists
? async function initializeRevalidation() {
let d = undefined
if (canRevalidate) {
if (url !== '') {
d = await fetchData({
query: Object.keys(reqQuery)
.map(q =>
Array.isArray(reqQuery[q])
? reqQuery[q]
.map((queryItem: any) => [q, queryItem].join('='))
.join('&')
: [q, reqQuery[q]].join('=')
)
.join('&'),
params: reqParams
})
} else {
d = def
// It means a url is not passed
setFetchState(prev => ({
...prev,
loading: false,
error:
hasErrors.get(resolvedDataKey) || hasErrors.get(resolvedKey),
completedAttempts: prev.completedAttempts
}))
}
} else {
d = def
}
return d
}
} else {
d = def
}
return d
},
: () => {
return new Promise((resolve, reject) => {
queue(() => resolve(initialDataValue))
})
},
[serialize(serialize(optionsConfig)), fetchState, thisDeps]
)

Expand Down Expand Up @@ -1371,17 +1381,23 @@ export function useFetch<FetchDataType = any, BodyType = any>(

if (suspense) {
if (auto) {
if (!suspenseInitialized.get(resolvedKey)) {
if (!suspenseRevalidationStarted.get(resolvedKey)) {
suspenseRevalidationStarted.set(resolvedKey, initializeRevalidation())
}

const w = suspenseRevalidationStarted.get(resolvedKey)
if (windowExists) {
if (!suspenseInitialized.get(resolvedKey)) {
if (!suspenseRevalidationStarted.get(resolvedKey)) {
suspenseRevalidationStarted.set(
resolvedKey,
initializeRevalidation()
)
}

suspenseInitialized.set(resolvedKey, false)
suspenseRevalidationStarted.delete(resolvedKey)
throw suspenseRevalidationStarted.get(resolvedKey)
}
}

throw w
if (!hasInitialOrFallbackData) {
throw new Error(
`Request with id "${id}" uses suspense but no SSF fallback data was provided. See https://httpr.vercel.app/docs/fetch_config/defaults`
)
}
}
}
Expand Down Expand Up @@ -1564,10 +1580,10 @@ export function useFetch<FetchDataType = any, BodyType = any>(
? $requestEnd
: null
: maxAge === 0
? null
: notNull(cacheProvider.get('expiration' + resolvedDataKey))
? new Date(cacheProvider.get('expiration' + resolvedDataKey))
: null
? null
: notNull(cacheProvider.get('expiration' + resolvedDataKey))
? new Date(cacheProvider.get('expiration' + resolvedDataKey))
: null

const isFailed =
hasErrors.get(resolvedDataKey) || hasErrors.get(resolvedKey) || error
Expand Down

0 comments on commit ab4c876

Please sign in to comment.