From 86df7545ea93fd17469181ddf32969177b431c08 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Thu, 24 Mar 2022 15:36:27 +0100 Subject: [PATCH] fix(useIsFetching): account for fetches happening in the same render cycle if a fetch happens in the same render cycle as useIsFetching is mounting, but it is mounted after the query, what happens is that: - the initial state of `isFetching` is `0` because the effect to fetch the query hasn't started yet - the query effect runs before the isFetching effect, which means we miss the event, thus the next event we get from the subscription gives us 0 again so we need to check for isFetching in the effect that runs to set up the subscription as well. This catches fetches that basically started in the same effect cycle, but before "our" effect ran --- src/react/tests/useIsFetching.test.tsx | 27 ++++++++++++++++++++++- src/react/useIsFetching.ts | 30 ++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/react/tests/useIsFetching.test.tsx b/src/react/tests/useIsFetching.test.tsx index 085242cbfd..16c83156fe 100644 --- a/src/react/tests/useIsFetching.test.tsx +++ b/src/react/tests/useIsFetching.test.tsx @@ -100,7 +100,7 @@ describe('useIsFetching', () => { } renderWithClient(queryClient, ) - await waitFor(() => expect(isFetchings).toEqual([0, 0, 1, 2, 1, 0])) + await waitFor(() => expect(isFetchings).toEqual([0, 1, 1, 2, 1, 0])) expect(consoleMock).not.toHaveBeenCalled() expect(consoleMock.mock.calls[0]?.[0] ?? '').not.toMatch('setState') @@ -159,4 +159,29 @@ describe('useIsFetching', () => { await sleep(100) expect(isFetchings).toEqual([0, 0, 1, 0]) }) + + it('should show the correct fetching state when mounted after a query', async () => { + const queryClient = new QueryClient() + const key = queryKey() + + function Page() { + useQuery(key, async () => { + await sleep(10) + return 'test' + }) + + const isFetching = useIsFetching() + + return ( +
+
isFetching: {isFetching}
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await rendered.findByText('isFetching: 1') + await rendered.findByText('isFetching: 0') + }) }) diff --git a/src/react/useIsFetching.ts b/src/react/useIsFetching.ts index a1319c53dd..b104fe1483 100644 --- a/src/react/useIsFetching.ts +++ b/src/react/useIsFetching.ts @@ -3,8 +3,21 @@ import React from 'react' import { notifyManager } from '../core/notifyManager' import { QueryKey } from '../core/types' import { parseFilterArgs, QueryFilters } from '../core/utils' +import { QueryClient } from '../core' import { useQueryClient } from './QueryClientProvider' +const checkIsFetching = ( + queryClient: QueryClient, + filters: QueryFilters, + isFetching: number, + setIsFetching: React.Dispatch> +) => { + const newIsFetching = queryClient.isFetching(filters) + if (isFetching !== newIsFetching) { + setIsFetching(newIsFetching) + } +} + export function useIsFetching(filters?: QueryFilters): number export function useIsFetching( queryKey?: QueryKey, @@ -31,13 +44,22 @@ export function useIsFetching( React.useEffect(() => { mountedRef.current = true + checkIsFetching( + queryClient, + filtersRef.current, + isFetchingRef.current, + setIsFetching + ) + const unsubscribe = queryClient.getQueryCache().subscribe( notifyManager.batchCalls(() => { if (mountedRef.current) { - const newIsFetching = queryClient.isFetching(filtersRef.current) - if (isFetchingRef.current !== newIsFetching) { - setIsFetching(newIsFetching) - } + checkIsFetching( + queryClient, + filtersRef.current, + isFetchingRef.current, + setIsFetching + ) } }) )