Skip to content

Commit a6186e5

Browse files
authored
feat(react-query-persist-client): await onuccess (#5473)
* 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 * test: test for awaiting onSuccess * docs: onSuccess
1 parent 7d6e6ec commit a6186e5

File tree

3 files changed

+69
-5
lines changed

3 files changed

+69
-5
lines changed

docs/react/plugins/persistQueryClient.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,10 +212,11 @@ ReactDOM.createRoot(rootElement).render(
212212

213213
- `persistOptions: PersistQueryClientOptions`
214214
- all [options](#options) you can pass to [persistQueryClient](#persistqueryclient) minus the QueryClient itself
215-
- `onSuccess?: () => void`
215+
- `onSuccess?: () => Promise<unknown> | unknown`
216216
- optional
217217
- will be called when the initial restore is finished
218218
- can be used to [resumePausedMutations](../reference/QueryClient#queryclientresumepausedmutations)
219+
- if a Promise is returned, it will be awaited; restoring is seen as ongoing until then
219220

220221
### useIsRestoring
221222

packages/react-query-persist-client/src/PersistQueryClientProvider.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { QueryClientProvider, IsRestoringProvider } from '@tanstack/react-query'
88

99
export type PersistQueryClientProviderProps = QueryClientProviderProps & {
1010
persistOptions: Omit<PersistQueryClientOptions, 'queryClient'>
11-
onSuccess?: () => void
11+
onSuccess?: () => Promise<unknown> | unknown
1212
}
1313

1414
export const PersistQueryClientProvider = ({
@@ -33,10 +33,13 @@ export const PersistQueryClientProvider = ({
3333
queryClient: client,
3434
})
3535

36-
promise.then(() => {
36+
promise.then(async () => {
3737
if (!isStale) {
38-
refs.current.onSuccess?.()
39-
setIsRestoring(false)
38+
try {
39+
await refs.current.onSuccess?.()
40+
} finally {
41+
setIsRestoring(false)
42+
}
4043
}
4144
})
4245

packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,66 @@ describe('PersistQueryClientProvider', () => {
401401
await waitFor(() => rendered.getByText('fetched'))
402402
})
403403

404+
test('should await onSuccess after successful restoring', async () => {
405+
const key = queryKey()
406+
407+
const queryClient = createQueryClient()
408+
await queryClient.prefetchQuery({
409+
queryKey: key,
410+
queryFn: () => Promise.resolve('hydrated'),
411+
})
412+
413+
const persister = createMockPersister()
414+
415+
await persistQueryClientSave({ queryClient, persister })
416+
417+
queryClient.clear()
418+
419+
const states: Array<string> = []
420+
421+
function Page() {
422+
const { data, fetchStatus } = useQuery({
423+
queryKey: key,
424+
queryFn: async () => {
425+
states.push('fetching')
426+
await sleep(10)
427+
states.push('fetched')
428+
return 'fetched'
429+
},
430+
})
431+
432+
return (
433+
<div>
434+
<h1>{data}</h1>
435+
<h2>fetchStatus: {fetchStatus}</h2>
436+
</div>
437+
)
438+
}
439+
440+
const rendered = render(
441+
<PersistQueryClientProvider
442+
client={queryClient}
443+
persistOptions={{ persister }}
444+
onSuccess={async () => {
445+
states.push('onSuccess')
446+
await sleep(20)
447+
states.push('onSuccess done')
448+
}}
449+
>
450+
<Page />
451+
</PersistQueryClientProvider>,
452+
)
453+
454+
await waitFor(() => rendered.getByText('hydrated'))
455+
await waitFor(() => rendered.getByText('fetched'))
456+
expect(states).toEqual([
457+
'onSuccess',
458+
'onSuccess done',
459+
'fetching',
460+
'fetched',
461+
])
462+
})
463+
404464
test('should remove cache after non-successful restoring', async () => {
405465
const key = queryKey()
406466
const consoleMock = vi.spyOn(console, 'error')

0 commit comments

Comments
 (0)