Skip to content

Commit 2b1c984

Browse files
authored
fix(react-query-persist-client): decouple subscribe and unsubscribe (#6028)
* fix(react-query-persist-client): decouple subscribe and unsubscribe to make sure subscriptions are always done at the end, even if we unsubscribe while we're restoring * chore: fix types * chore: I think this is better
1 parent 520f421 commit 2b1c984

File tree

2 files changed

+81
-16
lines changed

2 files changed

+81
-16
lines changed

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

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
'use client'
22
import * as React from 'react'
33

4-
import { persistQueryClient } from '@tanstack/query-persist-client-core'
4+
import {
5+
persistQueryClientRestore,
6+
persistQueryClientSubscribe,
7+
} from '@tanstack/query-persist-client-core'
58
import { IsRestoringProvider, QueryClientProvider } from '@tanstack/react-query'
69
import type { PersistQueryClientOptions } from '@tanstack/query-persist-client-core'
710
import type { QueryClientProviderProps } from '@tanstack/react-query'
@@ -27,25 +30,23 @@ export const PersistQueryClientProvider = ({
2730
})
2831

2932
React.useEffect(() => {
33+
const options = {
34+
...refs.current.persistOptions,
35+
queryClient: client,
36+
}
3037
if (!didRestore.current) {
3138
didRestore.current = true
3239
setIsRestoring(true)
33-
const [unsubscribe, promise] = persistQueryClient({
34-
...refs.current.persistOptions,
35-
queryClient: client,
36-
})
37-
38-
promise.then(() => {
39-
refs.current.onSuccess?.()
40-
setIsRestoring(false)
40+
persistQueryClientRestore(options).then(async () => {
41+
try {
42+
await refs.current.onSuccess?.()
43+
} finally {
44+
setIsRestoring(false)
45+
}
4146
})
42-
43-
return () => {
44-
unsubscribe()
45-
}
4647
}
47-
return undefined
48-
}, [client])
48+
return isRestoring ? undefined : persistQueryClientSubscribe(options)
49+
}, [client, isRestoring])
4950

5051
return (
5152
<QueryClientProvider client={client} {...props}>

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

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react'
2-
import { render, waitFor } from '@testing-library/react'
2+
import { fireEvent, render, waitFor } from '@testing-library/react'
33

44
import { QueryClient, useQueries, useQuery } from '@tanstack/react-query'
55
import { persistQueryClientSave } from '@tanstack/query-persist-client-core'
@@ -121,6 +121,70 @@ describe('PersistQueryClientProvider', () => {
121121
})
122122
})
123123

124+
test('should subscribe correctly in StrictMode', async () => {
125+
const key = queryKey()
126+
127+
const queryClient = createQueryClient()
128+
await queryClient.prefetchQuery({
129+
queryKey: key,
130+
queryFn: () => Promise.resolve('hydrated'),
131+
})
132+
133+
const persister = createMockPersister()
134+
135+
await persistQueryClientSave({ queryClient, persister })
136+
137+
queryClient.clear()
138+
139+
function Page() {
140+
const state = useQuery({
141+
queryKey: key,
142+
queryFn: async () => {
143+
await sleep(10)
144+
return 'fetched'
145+
},
146+
})
147+
148+
return (
149+
<div>
150+
<h1>{state.data}</h1>
151+
<h2>fetchStatus: {state.fetchStatus}</h2>
152+
<button
153+
onClick={() => {
154+
queryClient.setQueryData(key, 'updated')
155+
}}
156+
>
157+
update
158+
</button>
159+
</div>
160+
)
161+
}
162+
163+
const rendered = render(
164+
<React.StrictMode>
165+
<PersistQueryClientProvider
166+
client={queryClient}
167+
persistOptions={{ persister }}
168+
>
169+
<Page />
170+
</PersistQueryClientProvider>
171+
,
172+
</React.StrictMode>,
173+
)
174+
175+
await waitFor(() => rendered.getByText('fetchStatus: idle'))
176+
await waitFor(() => rendered.getByText('hydrated'))
177+
await waitFor(() => rendered.getByText('fetched'))
178+
179+
fireEvent.click(rendered.getByRole('button', { name: /update/i }))
180+
181+
await waitFor(() => rendered.getByText('updated'))
182+
183+
const state = await persister.restoreClient()
184+
185+
expect(state?.clientState.queries[0]?.state.data).toBe('updated')
186+
})
187+
124188
test('should also put useQueries into idle state', async () => {
125189
const key = queryKey()
126190
const states: UseQueryResult[] = []

0 commit comments

Comments
 (0)