From cb15b6af3dfb8d1e3801d6e85ac54a364784d7ae Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Fri, 26 May 2023 13:58:30 +0200 Subject: [PATCH 1/3] fix: have `onSuccess` return a promise too so that we can await it that way, invalidation or mutations that run in onSuccess will still keep isRestoring to be false, which avoids race conditions --- .../src/PersistQueryClientProvider.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/react-query-persist-client/src/PersistQueryClientProvider.tsx b/packages/react-query-persist-client/src/PersistQueryClientProvider.tsx index ecfc916a2b..fa4f8ec5b9 100644 --- a/packages/react-query-persist-client/src/PersistQueryClientProvider.tsx +++ b/packages/react-query-persist-client/src/PersistQueryClientProvider.tsx @@ -8,7 +8,7 @@ import { QueryClientProvider, IsRestoringProvider } from '@tanstack/react-query' export type PersistQueryClientProviderProps = QueryClientProviderProps & { persistOptions: Omit - onSuccess?: () => void + onSuccess?: () => Promise | unknown } export const PersistQueryClientProvider = ({ @@ -33,10 +33,13 @@ export const PersistQueryClientProvider = ({ queryClient: client, }) - promise.then(() => { + promise.then(async () => { if (!isStale) { - refs.current.onSuccess?.() - setIsRestoring(false) + try { + await refs.current.onSuccess?.() + } finally { + setIsRestoring(false) + } } }) From 6fb80194c6d12a701822b0e5b9111b94f5b20b1f Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Fri, 26 May 2023 14:03:16 +0200 Subject: [PATCH 2/3] test: test for awaiting onSuccess --- .../PersistQueryClientProvider.test.tsx | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx b/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx index 8c3a0c59c4..a9013eefab 100644 --- a/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx +++ b/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx @@ -401,6 +401,66 @@ describe('PersistQueryClientProvider', () => { await waitFor(() => rendered.getByText('fetched')) }) + test('should await onSuccess after successful restoring', async () => { + const key = queryKey() + + const queryClient = createQueryClient() + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => Promise.resolve('hydrated'), + }) + + const persister = createMockPersister() + + await persistQueryClientSave({ queryClient, persister }) + + queryClient.clear() + + const states: Array = [] + + function Page() { + const { data, fetchStatus } = useQuery({ + queryKey: key, + queryFn: async () => { + states.push('fetching') + await sleep(10) + states.push('fetched') + return 'fetched' + }, + }) + + return ( +
+

{data}

+

fetchStatus: {fetchStatus}

+
+ ) + } + + const rendered = render( + { + states.push('onSuccess') + await sleep(20) + states.push('onSuccess done') + }} + > + + , + ) + + await waitFor(() => rendered.getByText('hydrated')) + await waitFor(() => rendered.getByText('fetched')) + expect(states).toEqual([ + 'onSuccess', + 'onSuccess done', + 'fetching', + 'fetched', + ]) + }) + test('should remove cache after non-successful restoring', async () => { const key = queryKey() const consoleMock = vi.spyOn(console, 'error') From 6ba2dac0ef85f46ec76f5cdc3e19ac8f85fa5607 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Fri, 26 May 2023 15:10:03 +0200 Subject: [PATCH 3/3] docs: onSuccess --- docs/react/plugins/persistQueryClient.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/react/plugins/persistQueryClient.md b/docs/react/plugins/persistQueryClient.md index e673c33813..2f116599fc 100644 --- a/docs/react/plugins/persistQueryClient.md +++ b/docs/react/plugins/persistQueryClient.md @@ -212,10 +212,11 @@ ReactDOM.createRoot(rootElement).render( - `persistOptions: PersistQueryClientOptions` - all [options](#options) you can pass to [persistQueryClient](#persistqueryclient) minus the QueryClient itself -- `onSuccess?: () => void` +- `onSuccess?: () => Promise | unknown` - optional - will be called when the initial restore is finished - can be used to [resumePausedMutations](../reference/QueryClient#queryclientresumepausedmutations) + - if a Promise is returned, it will be awaited; restoring is seen as ongoing until then ### useIsRestoring