diff --git a/.changeset/fresh-colts-stay.md b/.changeset/fresh-colts-stay.md
new file mode 100644
index 000000000..1923cf7fb
--- /dev/null
+++ b/.changeset/fresh-colts-stay.md
@@ -0,0 +1,9 @@
+---
+"@scaleway/use-dataloader": major
+---
+
+- Changed `isLoading` behavior to be `true` only during the initial fetch when there's no cached data
+- `isLoading` is now `false` during subsequent fetches, even when `isFetching` is `true`
+- Added distinction between initial loading (`isLoading`) and ongoing fetching (`isFetching`)
+- `isLoading` is `true` only when there is no cache data and we're fetching for the first time
+- `isFetching` remains `true` during any active request (initial or subsequent)
diff --git a/.gitignore b/.gitignore
index 2a09aaa78..b388ec739 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,4 +35,8 @@ vitest.config.ts.*
# Yalc
yalc.lock
-.yalc/
\ No newline at end of file
+.yalc/
+
+# ai
+.ai
+AGENTS.md
diff --git a/packages/use-analytics/src/cookies-consent/CookieConsentProvider.tsx b/packages/use-analytics/src/cookies-consent/CookieConsentProvider.tsx
index dbb652b3d..1cfaba364 100644
--- a/packages/use-analytics/src/cookies-consent/CookieConsentProvider.tsx
+++ b/packages/use-analytics/src/cookies-consent/CookieConsentProvider.tsx
@@ -14,8 +14,8 @@ import {
CATEGORIES,
CONSENT_ADVERTISING_MAX_AGE,
CONSENT_MAX_AGE,
- COOKIES_OPTIONS,
COOKIE_PREFIX,
+ COOKIES_OPTIONS,
HASH_COOKIE,
} from '../constants'
import { uniq } from '../helpers/array'
diff --git a/packages/use-dataloader/README.md b/packages/use-dataloader/README.md
index f0311e21f..b6a1ca4a4 100644
--- a/packages/use-dataloader/README.md
+++ b/packages/use-dataloader/README.md
@@ -118,19 +118,19 @@ const fakePromise = () =>
function MyComponent() {
// Use a key if you want to persist data in the DataLoaderProvider cache
- const { data, isLoading, isSuccess, isError, error } = useDataLoader(
+ const { data, isLoading, isFetching, isSuccess, isError, error } = useDataLoader(
'cache-key',
fakePromise,
)
// This is the first time we load the data
if (isLoading && !data) {
- return
Loading...
+ return Loading initial data...
}
- // This happen when you already load the data but want to reload it
- if (isLoading && data) {
- return Reloading...
+ // This happen when you already loaded the data but want to reload it
+ if (isFetching && data) {
+ return Refreshing...
}
// Will be true when the promise is resolved
@@ -162,14 +162,19 @@ const fakePromise = () =>
function MyComponentThatUseDataLoader({key}) {
// Use a key if you want to persist data in the DataLoaderProvider cache
- const { data, isLoading, isSuccess, isError, error } = useDataLoader(
+ const { data, isLoading, isFetching, isSuccess, isError, error } = useDataLoader(
key,
fakePromise,
)
- // Will be true during the promise
+ // Will be true during the initial load
if (isLoading) {
- return Loading...
+ return Loading initial data...
+ }
+
+ // Will be true during any active request (initial or subsequent)
+ if (isFetching) {
+ return Refreshing...
}
// Will be true when the promise is resolved
@@ -236,10 +241,13 @@ const useDataLoader = (
)
```
+The hook returns an object with the following properties:
+
| Property | Description |
| :----------: | :------------------------------------------------------------------------------------------------------------------------------------------: |
| isIdle | `true` if the request is not launched |
-| isLoading | `true` if the request is launched **or** enabled is `true` and isIdle is `true` |
+| isLoading | `true` only during the initial fetch when there's no cached data |
+| isFetching | `true` when there is an active request in progress (initial or subsequent) |
| isSuccess | `true`if the request finished successfully |
| isError | `true` if the request throw an error |
| isPolling | `true` if the request if `enabled` is true, `pollingInterval` is defined and the status is `isLoading`,`isSuccess` or during the first fetch |
diff --git a/packages/use-dataloader/src/__tests__/useDataLoader.test.tsx b/packages/use-dataloader/src/__tests__/useDataLoader.test.tsx
index e71189b5f..b3e4c3b30 100644
--- a/packages/use-dataloader/src/__tests__/useDataLoader.test.tsx
+++ b/packages/use-dataloader/src/__tests__/useDataLoader.test.tsx
@@ -255,7 +255,7 @@ describe('useDataLoader', () => {
expect(result.current.isLoading).toBe(false)
result.current.reload().catch(() => null)
result.current.reload().catch(() => null)
- await waitFor(() => expect(result.current.isLoading).toBe(true))
+ await waitFor(() => expect(result.current.isFetching).toBe(true))
expect(result.current.data).toBe(true)
await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(result.current.data).toBe(true)
@@ -324,20 +324,21 @@ describe('useDataLoader', () => {
)
expect(result.current.data).toBe(undefined)
expect(result.current.isPolling).toBe(true)
- expect(result.current.isLoading).toBe(true)
+ expect(result.current.isFetching).toBe(true)
expect(pollingProps.method).toBeCalledTimes(1)
await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(result.current.data).toBe(true)
expect(result.current.isSuccess).toBe(true)
expect(result.current.isPolling).toBe(true)
- await waitFor(() => expect(result.current.isLoading).toBe(true))
+ await waitFor(() => expect(result.current.isFetching).toBe(true))
expect(pollingProps.method).toBeCalledTimes(2)
expect(result.current.isPolling).toBe(true)
await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(result.current.data).toBe(true)
+ expect(result.current.isPolling).toBe(true)
expect(result.current.isSuccess).toBe(true)
- expect(result.current.isLoading).toBe(false)
+ expect(result.current.isFetching).toBe(false)
rerender({
...pollingProps,
config: {
@@ -348,13 +349,13 @@ describe('useDataLoader', () => {
expect(result.current.data).toBe(true)
expect(result.current.isPolling).toBe(true)
expect(result.current.isSuccess).toBe(true)
- expect(result.current.isLoading).toBe(false)
- await waitFor(() => expect(result.current.isLoading).toBe(true))
+ expect(result.current.isFetching).toBe(false)
+ await waitFor(() => expect(result.current.isFetching).toBe(true))
expect(result.current.isSuccess).toBe(false)
await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(method2).toBeCalledTimes(1)
- expect(result.current.isPolling).toBe(true)
- expect(result.current.isLoading).toBe(false)
+ expect(result.current.isSuccess).toBe(true)
+ expect(result.current.isFetching).toBe(false)
expect(result.current.data).toBe(2)
rerender({
@@ -364,7 +365,7 @@ describe('useDataLoader', () => {
},
method: method2,
})
- await waitFor(() => expect(result.current.isLoading).toBe(true))
+ await waitFor(() => expect(result.current.isFetching).toBe(true))
expect(result.current.data).toBe(2)
expect(result.current.isPolling).toBe(true)
expect(result.current.isSuccess).toBe(false)
@@ -752,7 +753,7 @@ describe('useDataLoader', () => {
expect(result.current[0]?.data).toBe(true)
result.current[1]?.reload().catch(() => null)
- await waitFor(() => expect(result.current[1]?.isLoading).toBe(true))
+ await waitFor(() => expect(result.current[1]?.isFetching).toBe(true))
expect(result.current[1]?.data).toBe(true)
await waitFor(() => expect(result.current[1]?.isSuccess).toBe(true))
@@ -801,7 +802,7 @@ describe('useDataLoader', () => {
expect(mockedFn).toBeCalledTimes(1)
result.current[1].reloadAll().catch(() => null)
- await waitFor(() => expect(result.current[0].isLoading).toBe(true))
+ await waitFor(() => expect(result.current[0].isFetching).toBe(true))
expect(result.current[0].data).toBe(true)
expect(Object.values(result.current[1].getReloads()).length).toBe(1)
@@ -883,8 +884,8 @@ describe('useDataLoader', () => {
await waitFor(() => expect(result.current[0]?.isSuccess).toBe(true))
testingProps.config2.enabled = true
rerender(testingProps)
- await waitFor(() => expect(result.current[0]?.isLoading).toBe(true))
- await waitFor(() => expect(result.current[1]?.isLoading).toBe(true))
+ await waitFor(() => expect(result.current[0]?.isFetching).toBe(true))
+ await waitFor(() => expect(result.current[1]?.isFetching).toBe(true))
expect(testingProps.method).toBeCalledTimes(2)
expect(result.current[0]?.data).toBe(true)
expect(result.current[0]?.previousData).toBe(undefined)
@@ -893,4 +894,68 @@ describe('useDataLoader', () => {
await waitFor(() => expect(result.current[0]?.isSuccess).toBe(true))
await waitFor(() => expect(result.current[1]?.isSuccess).toBe(true))
})
+
+ test('should differentiate between isLoading and isFetching', async () => {
+ let resolveIt = false
+ const method = vi.fn(() => {
+ const promiseFn = () =>
+ new Promise(resolve => {
+ setInterval(() => {
+ if (resolveIt) {
+ resolve({ id: 1, name: 'test' })
+ }
+ }, PROMISE_TIMEOUT)
+ })
+
+ return promiseFn()
+ })
+
+ const testProps = {
+ config: {
+ enabled: true,
+ },
+ key: 'test-isLoading-vs-isFetching',
+ method,
+ }
+
+ const { result } = renderHook(
+ props => useDataLoader(props.key, props.method, props.config),
+ {
+ initialProps: testProps,
+ wrapper,
+ },
+ )
+
+ // Initially, isLoading should be true (first load with no cache)
+ expect(result.current.isLoading).toBe(true)
+ expect(result.current.isFetching).toBe(true)
+ expect(result.current.data).toBe(undefined)
+
+ // Resolve the first request
+ resolveIt = true
+ await waitFor(() => expect(result.current.isSuccess).toBe(true))
+
+ // After first load, both should be false
+ expect(result.current.isLoading).toBe(false)
+ expect(result.current.isFetching).toBe(false)
+ expect(result.current.data).toEqual({ id: 1, name: 'test' })
+
+ // Trigger a reload
+ resolveIt = false
+ result.current.reload().catch(() => null)
+
+ // During reload, isLoading should be false (we have cached data) but isFetching should be true
+ await waitFor(() => expect(result.current.isFetching).toBe(true))
+ expect(result.current.isLoading).toBe(false)
+ expect(result.current.data).toEqual({ id: 1, name: 'test' }) // Still have cached data
+
+ // Resolve the reload
+ resolveIt = true
+ await waitFor(() => expect(result.current.isSuccess).toBe(true))
+
+ // After reload, both should be false again
+ expect(result.current.isLoading).toBe(false)
+ expect(result.current.isFetching).toBe(false)
+ expect(result.current.data).toEqual({ id: 1, name: 'test' })
+ })
})
diff --git a/packages/use-dataloader/src/__tests__/useInfiniteDataLoader.test.tsx b/packages/use-dataloader/src/__tests__/useInfiniteDataLoader.test.tsx
index 9e785b3b1..b9313d34d 100644
--- a/packages/use-dataloader/src/__tests__/useInfiniteDataLoader.test.tsx
+++ b/packages/use-dataloader/src/__tests__/useInfiniteDataLoader.test.tsx
@@ -31,6 +31,7 @@ const getPrerequisite = (key: string) => {
}, 100)
}
}
+
resolvePromise()
}),
)
@@ -79,6 +80,7 @@ describe('useInfinitDataLoader', () => {
)
expect(result.current.data).toBe(undefined)
expect(result.current.isLoading).toBe(true)
+ expect(result.current.isFetching).toBe(true)
expect(initialProps.method).toHaveBeenCalledTimes(1)
setCanResolve(true)
await waitFor(() => expect(result.current.isSuccess).toBe(true))
@@ -87,6 +89,8 @@ describe('useInfinitDataLoader', () => {
{ nextPage: 2, data: 'Page 1 data' },
])
expect(result.current.isLoading).toBe(false)
+ expect(result.current.isLoading).toBe(false)
+ expect(result.current.isFetching).toBe(false)
})
it('should get the first and loadMore one page on mount while enabled', async () => {
@@ -107,6 +111,8 @@ describe('useInfinitDataLoader', () => {
)
expect(result.current.data).toBe(undefined)
expect(result.current.isLoading).toBe(true)
+ expect(result.current.isFetching).toBe(true)
+ expect(result.current.isFetching).toBe(true)
expect(initialProps.method).toHaveBeenCalledTimes(1)
expect(initialProps.method).toHaveBeenCalledWith({
page: 1,
@@ -118,6 +124,7 @@ describe('useInfinitDataLoader', () => {
{ nextPage: 2, data: 'Page 1 data' },
])
expect(result.current.isLoading).toBe(false)
+ expect(result.current.isFetching).toBe(false)
setCanResolve(false)
act(() => {
result.current.loadMore()
@@ -125,13 +132,15 @@ describe('useInfinitDataLoader', () => {
expect(result.current.data).toStrictEqual([
{ nextPage: 2, data: 'Page 1 data' },
])
- await waitFor(() => expect(result.current.isLoading).toBe(true))
+ await waitFor(() => expect(result.current.isFetching).toBe(true))
expect(initialProps.method).toHaveBeenCalledTimes(2)
expect(initialProps.method).toHaveBeenCalledWith({
page: 2,
})
setCanResolve(true)
+ await waitFor(() => expect(result.current.isFetching).toBe(true))
await waitFor(() => expect(result.current.isSuccess).toBe(true))
+ await waitFor(() => expect(result.current.isFetching).toBe(false))
expect(result.current.data).toStrictEqual([
{ nextPage: 2, data: 'Page 1 data' },
{ nextPage: 3, data: 'Page 2 data' },
@@ -157,6 +166,7 @@ describe('useInfinitDataLoader', () => {
)
expect(result.current.data).toBe(undefined)
expect(result.current.isLoading).toBe(true)
+ expect(result.current.isFetching).toBe(true)
expect(initialProps.method).toHaveBeenCalledTimes(1)
expect(initialProps.method).toHaveBeenCalledWith({
page: 1,
@@ -169,10 +179,11 @@ describe('useInfinitDataLoader', () => {
{ nextPage: 2, data: 'Page 1 data' },
])
expect(result.current.isLoading).toBe(false)
+ expect(result.current.isFetching).toBe(false)
act(() => {
result.current.loadMore()
})
- await waitFor(() => expect(result.current.isLoading).toBe(true))
+ await waitFor(() => expect(result.current.isFetching).toBe(true))
expect(result.current.data).toStrictEqual([
{ nextPage: 2, data: 'Page 1 data' },
])
@@ -191,7 +202,7 @@ describe('useInfinitDataLoader', () => {
act(() => {
result.current.reload().catch(() => null)
})
- await waitFor(() => expect(result.current.isLoading).toBe(true))
+ await waitFor(() => expect(result.current.isFetching).toBe(true))
setCanResolve(true)
await waitFor(() => expect(result.current.isSuccess).toBe(true))
expect(result.current.data).toStrictEqual([
@@ -226,14 +237,16 @@ describe('useInfinitDataLoader', () => {
)
expect(result.current.data).toBe(undefined)
expect(result.current.isLoading).toBe(false)
+ expect(result.current.isFetching).toBe(false)
expect(initialProps.method).toHaveBeenCalledTimes(0)
rerender(localInitialProps)
expect(result.current.data).toBe(undefined)
expect(result.current.isLoading).toBe(false)
+ expect(result.current.isFetching).toBe(false)
expect(initialProps.method).toHaveBeenCalledTimes(0)
rerender({ ...localInitialProps, config: { ...config, enabled: true } })
expect(result.current.data).toBe(undefined)
- await waitFor(() => expect(result.current.isLoading).toBe(true))
+ await waitFor(() => expect(result.current.isFetching).toBe(true))
expect(initialProps.method).toHaveBeenCalledTimes(1)
expect(initialProps.method).toHaveBeenCalledWith({
page: 1,
@@ -246,10 +259,11 @@ describe('useInfinitDataLoader', () => {
{ nextPage: 2, data: 'Page 1 data' },
])
expect(result.current.isLoading).toBe(false)
+ expect(result.current.isFetching).toBe(false)
act(() => {
result.current.loadMore()
})
- await waitFor(() => expect(result.current.isLoading).toBe(true))
+ await waitFor(() => expect(result.current.isFetching).toBe(true))
expect(result.current.data).toStrictEqual([
{ nextPage: 2, data: 'Page 1 data' },
])
@@ -258,7 +272,12 @@ describe('useInfinitDataLoader', () => {
page: 2,
})
setCanResolve(true)
+ await waitFor(() => expect(result.current.isFetching).toBe(true))
await waitFor(() => expect(result.current.isSuccess).toBe(true))
+ await waitFor(() => expect(result.current.isLoading).toBe(false))
+ // After loadMore completes, we should still have only 2 calls
+ // (initial load + loadMore), not 3, because reload reuses existing requests
+ expect(initialProps.method).toHaveBeenCalledTimes(2)
expect(result.current.data).toStrictEqual([
{ nextPage: 2, data: 'Page 1 data' },
{ nextPage: 3, data: 'Page 2 data' },
@@ -268,9 +287,71 @@ describe('useInfinitDataLoader', () => {
act(() => {
result.current.reload().catch(() => null)
})
- await waitFor(() => expect(result.current.isLoading).toBe(true))
+ await waitFor(() => expect(result.current.isFetching).toBe(true))
+ setCanResolve(true)
+ await waitFor(() => expect(result.current.isFetching).toBe(false))
+ expect(result.current.data).toStrictEqual([
+ { nextPage: 2, data: 'Page 1 data' },
+ { nextPage: 3, data: 'Page 2 data' },
+ ])
+ })
+
+ it('should differentiate between isLoading and isFetching', async () => {
+ const { setCanResolve, initialProps } = getPrerequisite(
+ 'test-isLoading-vs-isFetching',
+ )
+
+ const { result } = renderHook(
+ props =>
+ useInfiniteDataLoader(
+ props.key,
+ props.method,
+ props.baseParams,
+ 'page',
+ config,
+ ),
+ {
+ initialProps,
+ wrapper,
+ },
+ )
+
+ // Initially, isLoading should be true (first load with no cache)
+ expect(result.current.isLoading).toBe(true)
+ expect(result.current.isFetching).toBe(true)
+ expect(result.current.data).toBe(undefined)
+
+ // Resolve the first request
+ setCanResolve(true)
+ await waitFor(() => expect(result.current.isSuccess).toBe(true))
+
+ // After first load, isLoading should be false but isFetching should also be false
+ expect(result.current.isLoading).toBe(false)
+ expect(result.current.isFetching).toBe(false)
+ expect(result.current.data).toStrictEqual([
+ { nextPage: 2, data: 'Page 1 data' },
+ ])
+
+ // Trigger a loadMore
+ setCanResolve(false)
+ act(() => {
+ result.current.loadMore()
+ })
+
+ // During loadMore, isLoading should be false (we have cached data) but isFetching should be true
+ await waitFor(() => expect(result.current.isFetching).toBe(true))
+ expect(result.current.isLoading).toBe(false)
+ expect(result.current.data).toStrictEqual([
+ { nextPage: 2, data: 'Page 1 data' },
+ ]) // Still have cached data
+
+ // Resolve the loadMore
setCanResolve(true)
await waitFor(() => expect(result.current.isSuccess).toBe(true))
+
+ // After loadMore, both should be false again
+ expect(result.current.isLoading).toBe(false)
+ expect(result.current.isFetching).toBe(false)
expect(result.current.data).toStrictEqual([
{ nextPage: 2, data: 'Page 1 data' },
{ nextPage: 3, data: 'Page 2 data' },
diff --git a/packages/use-dataloader/src/types.ts b/packages/use-dataloader/src/types.ts
index a1dca905f..c0378cf1a 100644
--- a/packages/use-dataloader/src/types.ts
+++ b/packages/use-dataloader/src/types.ts
@@ -73,9 +73,13 @@ export type UseDataLoaderResult = {
*/
isIdle: boolean
/**
- * True if the request is launched
+ * True only when there is no cache data and we're fetching data for the first time
*/
isLoading: boolean
+ /**
+ * True if there is an active request in progress
+ */
+ isFetching: boolean
/**
* True if the request if enabled is true, pollingInterval is defined and the status is isLoading or isSuccess
*/
@@ -119,9 +123,10 @@ export type UseInfiniteDataLoaderResult = {
isError: boolean
isIdle: boolean
isLoading: boolean
- isLoadingFirstPage: boolean
+ isFetching: boolean
isSuccess: boolean
hasNextPage: boolean
+ isLoadingFirstPage: boolean
reload: () => Promise
loadMore: () => void
}
diff --git a/packages/use-dataloader/src/useDataLoader.ts b/packages/use-dataloader/src/useDataLoader.ts
index 49ccd7e7c..bd16efb09 100644
--- a/packages/use-dataloader/src/useDataLoader.ts
+++ b/packages/use-dataloader/src/useDataLoader.ts
@@ -71,7 +71,17 @@ export const useDataLoader = (
const previousDataRef = useRef(request.data)
+ // Compute the data that will be returned to the user
+ const computedData = !request.isFirstLoading ? request.data : initialData
+
+ // isLoading is true only when there is no cache data and we're fetching data for the first time
const isLoading =
+ !computedData &&
+ request.isFirstLoading &&
+ (request.status === StatusEnum.LOADING || optimisticIsLoadingRef.current)
+
+ // isFetching is true when there is an active request in progress
+ const isFetching =
request.status === StatusEnum.LOADING || optimisticIsLoadingRef.current
const isSuccess = request.status === StatusEnum.SUCCESS
@@ -87,11 +97,26 @@ export const useDataLoader = (
(typeof needPolling !== 'function' && needPolling))
)
- const reload = useCallback(
- () =>
- request.load(true).then(onSuccessRef.current).catch(onErrorRef.current),
- [request],
- )
+ const reload: () => Promise = useCallback(async () => {
+ // Set optimistic loading state to true when reload is called
+ optimisticIsLoadingRef.current = true
+
+ const onSuccessHandler = (result: ResultType) => {
+ // Set optimistic loading state to false when request completes
+ optimisticIsLoadingRef.current = false
+
+ return onSuccessRef.current?.(result)
+ }
+
+ const onErrorHandler = (error: ErrorType & Error) => {
+ // Set optimistic loading state to false when request completes
+ optimisticIsLoadingRef.current = false
+
+ return onErrorRef.current?.(error)
+ }
+
+ return request.load(true).then(onSuccessHandler).catch(onErrorHandler)
+ }, [request])
useEffect(() => {
needPollingRef.current = needPolling
@@ -117,12 +142,17 @@ export const useDataLoader = (
useEffect(() => {
if (needLoad) {
- const defaultOnSuccessOrError = () => {}
+ // Set optimistic loading state to true when auto-loading is triggered
+ optimisticIsLoadingRef.current = true
+
+ const defaultOnSuccessOrError = () => {
+ // Set optimistic loading state to false when request completes
+ optimisticIsLoadingRef.current = false
+ }
const onSuccessLoad = onSuccessRef.current ?? defaultOnSuccessOrError
const onFailedLoad = onErrorRef.current ?? defaultOnSuccessOrError
request.load().then(onSuccessLoad).catch(onFailedLoad)
}
- optimisticIsLoadingRef.current = false
}, [needLoad, request])
useEffect(() => {
@@ -155,9 +185,10 @@ export const useDataLoader = (
}, [pollingInterval, request])
return {
- data: !request.isFirstLoading ? request.data : initialData,
+ data: computedData,
error: request.error,
isError,
+ isFetching,
isIdle,
isLoading,
isPolling,
diff --git a/packages/use-dataloader/src/useInfiniteDataLoader.ts b/packages/use-dataloader/src/useInfiniteDataLoader.ts
index bac496dbc..214cf542e 100644
--- a/packages/use-dataloader/src/useInfiniteDataLoader.ts
+++ b/packages/use-dataloader/src/useInfiniteDataLoader.ts
@@ -152,7 +152,8 @@ export const useInfiniteDataLoader = <
const previousDataRef = useRef(request.data)
- const isLoading = requestRefs.current.some(
+ // isFetching is true when there is an active request in progress
+ const isFetching = requestRefs.current.some(
req => req.status === StatusEnum.LOADING || optimisticIsLoadingRef.current,
)
@@ -236,19 +237,28 @@ export const useInfiniteDataLoader = <
getNextPage ? getNextPage(...params) : undefined
}, [getNextPage])
+ const computedData =
+ isLoadingFirstPage ||
+ [...requestRefs.current].filter(dataloader => !!dataloader.data).length ===
+ 0
+ ? initialData
+ : ([...requestRefs.current]
+ .filter(dataloader => !!dataloader.data)
+ .map(dataloader => dataloader.data) as ResultType[])
+
+ // isLoading is true only when there is no cache data and we're fetching data for the first time
+ const isLoading =
+ !computedData &&
+ request.isFirstLoading &&
+ request.status === StatusEnum.LOADING
+
const data = useMemo>(
() => ({
- data:
- isLoadingFirstPage ||
- [...requestRefs.current].filter(dataloader => !!dataloader.data)
- .length === 0
- ? initialData
- : ([...requestRefs.current]
- .filter(dataloader => !!dataloader.data)
- .map(dataloader => dataloader.data) as ResultType[]),
+ data: computedData,
error: request.error,
hasNextPage: nextPageRef.current !== undefined,
isError,
+ isFetching,
isIdle,
isLoading,
isLoadingFirstPage,
@@ -257,9 +267,10 @@ export const useInfiniteDataLoader = <
reload,
}),
[
- initialData,
+ computedData,
isIdle,
isLoading,
+ isFetching,
isSuccess,
isError,
request.error,