Skip to content

Commit a5b2216

Browse files
authored
refactor: use react-state for one-time initialization over instance refs (TanStack#2253)
1 parent ff830db commit a5b2216

File tree

4 files changed

+28
-42
lines changed

4 files changed

+28
-42
lines changed

docs/src/pages/guides/ssr.md

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ React Query supports prefetching multiple queries on the server in Next.js and t
4949

5050
To support caching queries on the server and set up hydration:
5151

52-
- Create a new `QueryClient` instance **inside of your app, and on an instance ref. This ensures that data is not shared between different users and requests.**
52+
- Create a new `QueryClient` instance **inside of your app, and on an instance ref (or in React state). This ensures that data is not shared between different users and requests, while still only creating the QueryClient once per component lifecycle.**
5353
- Wrap your app component with `<QueryClientProvider>` and pass it the client instance
5454
- Wrap your app component with `<Hydrate>` and pass it the `dehydratedState` prop from `pageProps`
5555

@@ -59,13 +59,10 @@ import { QueryClient, QueryClientProvider } from 'react-query'
5959
import { Hydrate } from 'react-query/hydration'
6060

6161
export default function MyApp({ Component, pageProps }) {
62-
const queryClientRef = React.useRef()
63-
if (!queryClientRef.current) {
64-
queryClientRef.current = new QueryClient()
65-
}
62+
const [queryClient] = React.useState(() => new QueryClient())
6663

6764
return (
68-
<QueryClientProvider client={queryClientRef.current}>
65+
<QueryClientProvider client={queryClient}>
6966
<Hydrate state={pageProps.dehydratedState}>
7067
<Component {...pageProps} />
7168
</Hydrate>

examples/nextjs/pages/_app.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@ import { Hydrate } from 'react-query/hydration'
44
import { ReactQueryDevtools } from 'react-query/devtools'
55

66
export default function MyApp({ Component, pageProps }) {
7-
const queryClientRef = React.useRef()
8-
if (!queryClientRef.current) {
9-
queryClientRef.current = new QueryClient()
10-
}
7+
const [queryClient] = React.useState(() => new QueryClient())
118

129
return (
13-
<QueryClientProvider client={queryClientRef.current}>
10+
<QueryClientProvider client={queryClient}>
1411
<Hydrate state={pageProps.dehydratedState}>
1512
<Component {...pageProps} />
1613
</Hydrate>

src/react/useBaseQuery.ts

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -67,28 +67,22 @@ export function useBaseQuery<
6767
}
6868
}
6969

70-
const obsRef = React.useRef<
71-
QueryObserver<TQueryFnData, TError, TData, TQueryData, TQueryKey>
72-
>()
73-
74-
if (!obsRef.current) {
75-
obsRef.current = new Observer<
76-
TQueryFnData,
77-
TError,
78-
TData,
79-
TQueryData,
80-
TQueryKey
81-
>(queryClient, defaultedOptions)
82-
}
70+
const [observer] = React.useState(
71+
() =>
72+
new Observer<TQueryFnData, TError, TData, TQueryData, TQueryKey>(
73+
queryClient,
74+
defaultedOptions
75+
)
76+
)
8377

84-
let result = obsRef.current.getOptimisticResult(defaultedOptions)
78+
let result = observer.getOptimisticResult(defaultedOptions)
8579

8680
React.useEffect(() => {
8781
mountedRef.current = true
8882

8983
errorResetBoundary.clearReset()
9084

91-
const unsubscribe = obsRef.current!.subscribe(
85+
const unsubscribe = observer.subscribe(
9286
notifyManager.batchCalls(() => {
9387
if (mountedRef.current) {
9488
forceUpdate(x => x + 1)
@@ -98,23 +92,23 @@ export function useBaseQuery<
9892

9993
// Update result to make sure we did not miss any query updates
10094
// between creating the observer and subscribing to it.
101-
obsRef.current!.updateResult()
95+
observer.updateResult()
10296

10397
return () => {
10498
mountedRef.current = false
10599
unsubscribe()
106100
}
107-
}, [errorResetBoundary])
101+
}, [errorResetBoundary, observer])
108102

109103
React.useEffect(() => {
110104
// Do not notify on updates because of changes in the options because
111105
// these changes should already be reflected in the optimistic result.
112-
obsRef.current!.setOptions(defaultedOptions, { listeners: false })
113-
}, [defaultedOptions])
106+
observer.setOptions(defaultedOptions, { listeners: false })
107+
}, [defaultedOptions, observer])
114108

115109
// Handle suspense
116110
if (defaultedOptions.suspense && result.isLoading) {
117-
throw obsRef.current
111+
throw observer
118112
.fetchOptimistic(defaultedOptions)
119113
.then(({ data }) => {
120114
defaultedOptions.onSuccess?.(data as TData)
@@ -138,7 +132,7 @@ export function useBaseQuery<
138132

139133
// Handle result property usage tracking
140134
if (defaultedOptions.notifyOnChangeProps === 'tracked') {
141-
result = obsRef.current.trackResult(result)
135+
result = observer.trackResult(result)
142136
}
143137

144138
return result

src/react/useQueries.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,16 @@ export function useQueries(queries: UseQueryOptions[]): UseQueryResult[] {
2020
return defaultedOptions
2121
})
2222

23-
const obsRef = React.useRef<QueriesObserver>()
23+
const [observer] = React.useState(
24+
() => new QueriesObserver(queryClient, defaultedQueries)
25+
)
2426

25-
if (!obsRef.current) {
26-
obsRef.current = new QueriesObserver(queryClient, defaultedQueries)
27-
}
28-
29-
const result = obsRef.current.getOptimisticResult(defaultedQueries)
27+
const result = observer.getOptimisticResult(defaultedQueries)
3028

3129
React.useEffect(() => {
3230
mountedRef.current = true
3331

34-
const unsubscribe = obsRef.current!.subscribe(
32+
const unsubscribe = observer.subscribe(
3533
notifyManager.batchCalls(() => {
3634
if (mountedRef.current) {
3735
forceUpdate(x => x + 1)
@@ -43,13 +41,13 @@ export function useQueries(queries: UseQueryOptions[]): UseQueryResult[] {
4341
mountedRef.current = false
4442
unsubscribe()
4543
}
46-
}, [])
44+
}, [observer])
4745

4846
React.useEffect(() => {
4947
// Do not notify on updates because of changes in the options because
5048
// these changes should already be reflected in the optimistic result.
51-
obsRef.current!.setQueries(defaultedQueries, { listeners: false })
52-
}, [defaultedQueries])
49+
observer.setQueries(defaultedQueries, { listeners: false })
50+
}, [defaultedQueries, observer])
5351

5452
return result
5553
}

0 commit comments

Comments
 (0)