Skip to content

Commit 0d2c2e0

Browse files
authored
fix(react-query): move hydrationQueue from state to a derived value (#9247)
1 parent 058bfc9 commit 0d2c2e0

File tree

1 file changed

+41
-51
lines changed

1 file changed

+41
-51
lines changed

packages/react-query/src/HydrationBoundary.tsx

Lines changed: 41 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ export const HydrationBoundary = ({
2929
queryClient,
3030
}: HydrationBoundaryProps) => {
3131
const client = useQueryClient(queryClient)
32-
const [hydrationQueue, setHydrationQueue] = React.useState<
33-
DehydratedState['queries'] | undefined
34-
>()
3532

3633
const optionsRef = React.useRef(options)
3734
optionsRef.current = options
@@ -51,66 +48,59 @@ export const HydrationBoundary = ({
5148
// If the transition is aborted, we will have hydrated any _new_ queries, but
5249
// we throw away the fresh data for any existing ones to avoid unexpectedly
5350
// updating the UI.
54-
React.useMemo(() => {
55-
if (state) {
56-
if (typeof state !== 'object') {
57-
return
58-
}
59-
60-
const queryCache = client.getQueryCache()
61-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
62-
const queries = (state as DehydratedState).queries || []
63-
64-
const newQueries: DehydratedState['queries'] = []
65-
const existingQueries: DehydratedState['queries'] = []
66-
for (const dehydratedQuery of queries) {
67-
const existingQuery = queryCache.get(dehydratedQuery.queryHash)
51+
const hydrationQueue: DehydratedState['queries'] | undefined =
52+
React.useMemo(() => {
53+
if (state) {
54+
if (typeof state !== 'object') {
55+
return
56+
}
6857

69-
if (!existingQuery) {
70-
newQueries.push(dehydratedQuery)
71-
} else {
72-
const hydrationIsNewer =
73-
dehydratedQuery.state.dataUpdatedAt >
74-
existingQuery.state.dataUpdatedAt ||
75-
(dehydratedQuery.promise &&
76-
existingQuery.state.status !== 'pending' &&
77-
existingQuery.state.fetchStatus !== 'fetching' &&
78-
dehydratedQuery.dehydratedAt !== undefined &&
79-
dehydratedQuery.dehydratedAt > existingQuery.state.dataUpdatedAt)
58+
const queryCache = client.getQueryCache()
59+
// State is supplied from the outside and we might as well fail
60+
// gracefully if it has the wrong shape, so while we type `queries`
61+
// as required, we still provide a fallback.
62+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
63+
const queries = (state as DehydratedState).queries || []
8064

81-
const queryAlreadyQueued = hydrationQueue?.find(
82-
(query) => query.queryHash === dehydratedQuery.queryHash,
83-
)
65+
const newQueries: DehydratedState['queries'] = []
66+
const existingQueries: DehydratedState['queries'] = []
67+
for (const dehydratedQuery of queries) {
68+
const existingQuery = queryCache.get(dehydratedQuery.queryHash)
8469

85-
if (
86-
hydrationIsNewer &&
87-
(!queryAlreadyQueued ||
70+
if (!existingQuery) {
71+
newQueries.push(dehydratedQuery)
72+
} else {
73+
const hydrationIsNewer =
8874
dehydratedQuery.state.dataUpdatedAt >
89-
queryAlreadyQueued.state.dataUpdatedAt)
90-
) {
91-
existingQueries.push(dehydratedQuery)
75+
existingQuery.state.dataUpdatedAt ||
76+
(dehydratedQuery.promise &&
77+
existingQuery.state.status !== 'pending' &&
78+
existingQuery.state.fetchStatus !== 'fetching' &&
79+
dehydratedQuery.dehydratedAt !== undefined &&
80+
dehydratedQuery.dehydratedAt >
81+
existingQuery.state.dataUpdatedAt)
82+
83+
if (hydrationIsNewer) {
84+
existingQueries.push(dehydratedQuery)
85+
}
9286
}
9387
}
94-
}
9588

96-
if (newQueries.length > 0) {
97-
// It's actually fine to call this with queries/state that already exists
98-
// in the cache, or is older. hydrate() is idempotent for queries.
99-
hydrate(client, { queries: newQueries }, optionsRef.current)
100-
}
101-
if (existingQueries.length > 0) {
102-
// eslint-disable-next-line react-hooks/react-compiler
103-
setHydrationQueue((prev) =>
104-
prev ? [...prev, ...existingQueries] : existingQueries,
105-
)
89+
if (newQueries.length > 0) {
90+
// It's actually fine to call this with queries/state that already exists
91+
// in the cache, or is older. hydrate() is idempotent for queries.
92+
hydrate(client, { queries: newQueries }, optionsRef.current)
93+
}
94+
if (existingQueries.length > 0) {
95+
return existingQueries
96+
}
10697
}
107-
}
108-
}, [client, hydrationQueue, state])
98+
return undefined
99+
}, [client, state])
109100

110101
React.useEffect(() => {
111102
if (hydrationQueue) {
112103
hydrate(client, { queries: hydrationQueue }, optionsRef.current)
113-
setHydrationQueue(undefined)
114104
}
115105
}, [client, hydrationQueue])
116106

0 commit comments

Comments
 (0)