Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.8.2",
"version": "3.8.3",
"description": "React hooks for data fetching",
"main": "dist/index.js",
"scripts": {
Expand Down
133 changes: 53 additions & 80 deletions src/hooks/use-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import {
createImperativeFetch,
getMiliseconds,
getTimePassed,
mutateData,
revalidate,
useIsomorphicLayoutEffect
} from '../utils'
Expand Down Expand Up @@ -1040,98 +1041,53 @@ export function useFetch<FetchDataType = any, TransformData = any>(
]
)

// At the top of your component, create a ref to hold all dependencies for the listener.
const listenerDeps = useRef({
thisDeps,
idString,
requestCallId,
forceMutate,
imperativeFetch,
setData,
setOnline,
setLoading,
setError,
setCompletedAttempts,
handleMutate,
url,
onMutate
})

// On every render, update the ref's current value to ensure the listener always has the latest functions and props.
// This does not trigger the effect below.
listenerDeps.current = {
thisDeps,
idString,
requestCallId,
forceMutate,
imperativeFetch,
setData,
setOnline,
setLoading,
setError,
setCompletedAttempts,
handleMutate,
url,
onMutate
}

useEffect(() => {
function waitFormUpdates(event: any) {
// Destructure the latest dependencies from the ref inside the listener
const deps = listenerDeps.current
function waitFormUpdates(v: any) {
const {
isMutating,
data: $data,
error: $error,
online,
loading,
completedAttempts
} = event || {}
} = v || {}

// 1. Handle mutations with cleaner logic
if (
isMutating &&
serialize($data) !== serialize(cacheForMutation.get(resolvedKey))
) {
cacheForMutation.set(deps.idString, $data)
deps.forceMutate($data)

if (deps.handleMutate) {
const canCallOnMutate =
deps.url === '' || !runningMutate.get(resolvedKey)
if (canCallOnMutate) {
if (deps.url !== '') runningMutate.set(resolvedKey, true)
;(deps.onMutate as any)($data, deps.imperativeFetch)
}
}
}
if (isMutating) {
if (!jsonCompare($data, cacheForMutation.get(resolvedKey))) {
cacheForMutation.set(idString, $data)

// 2. Handle state synchronization from other hooks
if (
event.requestCallId !== deps.requestCallId &&
!willSuspend.get(resolvedKey)
) {
// Create a map to avoid the long if-chain
const stateUpdates = {
data: { value: $data, setter: deps.setData },
online: { value: online, setter: deps.setOnline },
loading: { value: loading, setter: deps.setLoading },
error: { value: $error, setter: deps.setError },
completedAttempts: {
value: completedAttempts,
setter: deps.setCompletedAttempts
if (isMutating) {
if (handleMutate) {
if (url === '') {
;(onMutate as any)($data, imperativeFetch)
} else {
if (!runningMutate.get(resolvedKey)) {
runningMutate.set(resolvedKey, true)
;(onMutate as any)($data, imperativeFetch)
}
}
}
}
}
}

// Loop through the map to update state concisely using a transition
startTransition(() => {
for (const key in stateUpdates) {
const update = stateUpdates[key as keyof typeof stateUpdates]
if (
deps.thisDeps[key as keyof typeof stateUpdates] &&
isDefined(update.value)
) {
update.setter(update.value)
if (v.requestCallId !== requestCallId) {
queue(() => {
if (!willSuspend.get(resolvedKey)) {
if (inDeps('data') && isDefined($data)) {
setData($data)
}
if (inDeps('online') && isDefined(online)) {
setOnline(online)
}
if (inDeps('loading') && isDefined(loading)) {
setLoading(loading)
}
if (inDeps('error') && isDefined($error)) {
setError($error)
}
if (inDeps('completedAttempts') && isDefined(completedAttempts)) {
setCompletedAttempts(completedAttempts)
}
}
})
Expand All @@ -1143,7 +1099,22 @@ export function useFetch<FetchDataType = any, TransformData = any>(
return () => {
requestsProvider.removeListener(resolvedKey, waitFormUpdates)
}
}, [resolvedKey])
}, [
// thisDeps,
resolvedKey,
idString,
requestCallId,
forceMutate,
imperativeFetch,
setData,
setOnline,
setLoading,
setError,
setCompletedAttempts,
handleMutate,
url,
onMutate
])

const reValidate = useCallback(
async function reValidate() {
Expand Down Expand Up @@ -1307,6 +1278,7 @@ export function useFetch<FetchDataType = any, TransformData = any>(

useEffect(() => {
// Attempts will be made after a request fails

const tm = setTimeout(() => {
if (!gettingAttempts.get(resolvedKey)) {
gettingAttempts.set(resolvedKey, true)
Expand Down Expand Up @@ -1727,6 +1699,7 @@ Learn more: https://httpr.vercel.app/docs/api#suspense
submit,
get mutate() {
thisDeps.data = true

return forceMutate
},
get fetcher() {
Expand Down
12 changes: 7 additions & 5 deletions src/utils/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,14 @@ function canHaveBody(method: keyof typeof METHODS) {
}

export function queue(callback: any, time: number = 0) {
const tm = setTimeout(() => {
callback();
clearTimeout(tm);
}, time);
queueMicrotask(callback);

return tm;
// const tm = setTimeout(() => {
// callback();
// clearTimeout(tm);
// }, time);

// return tm;
}

/**
Expand Down