diff --git a/.changeset/salty-hotels-turn.md b/.changeset/salty-hotels-turn.md
new file mode 100644
index 0000000000..8cb3b5d247
--- /dev/null
+++ b/.changeset/salty-hotels-turn.md
@@ -0,0 +1,6 @@
+---
+'@tanstack/preact-query': patch
+'@tanstack/react-query': patch
+---
+
+ref(HydrationBoundary): remove checks that are guarded by types
diff --git a/packages/preact-query/src/HydrationBoundary.tsx b/packages/preact-query/src/HydrationBoundary.tsx
index f9cf967b06..101ebb9860 100644
--- a/packages/preact-query/src/HydrationBoundary.tsx
+++ b/packages/preact-query/src/HydrationBoundary.tsx
@@ -18,7 +18,7 @@ export interface HydrationBoundaryProps {
/**
* The state to hydrate.
*/
- state: DehydratedState | null | undefined
+ state: DehydratedState
/**
* Optional. Note: unlike `hydrate`, `mutations` cannot be set here.
*/
@@ -113,49 +113,36 @@ export const HydrationBoundary = ({
// we throw away the fresh data for any existing ones to avoid unexpectedly
// updating the UI.
const hydrationQueue: DehydratedState['queries'] | undefined = useMemo(() => {
- if (state) {
- if (typeof state !== 'object') {
- return
- }
-
- const queryCache = client.getQueryCache()
- // State is supplied from the outside and we might as well fail
- // gracefully if it has the wrong shape, so while we type `queries`
- // as required, we still provide a fallback.
- const queries = state.queries || []
+ const queryCache = client.getQueryCache()
- const newQueries: DehydratedState['queries'] = []
- const existingQueries: DehydratedState['queries'] = []
- for (const dehydratedQuery of queries) {
- const existingQuery = queryCache.get(dehydratedQuery.queryHash)
+ const newQueries: DehydratedState['queries'] = []
+ const existingQueries: DehydratedState['queries'] = []
+ for (const dehydratedQuery of state.queries) {
+ const existingQuery = queryCache.get(dehydratedQuery.queryHash)
- if (!existingQuery) {
- newQueries.push(dehydratedQuery)
- } else {
- const hydrationIsNewer =
- dehydratedQuery.state.dataUpdatedAt >
- existingQuery.state.dataUpdatedAt ||
- (dehydratedQuery.promise &&
- existingQuery.state.status !== 'pending' &&
- existingQuery.state.fetchStatus !== 'fetching' &&
- dehydratedQuery.dehydratedAt > existingQuery.state.dataUpdatedAt)
+ if (!existingQuery) {
+ newQueries.push(dehydratedQuery)
+ } else {
+ const hydrationIsNewer =
+ dehydratedQuery.state.dataUpdatedAt >
+ existingQuery.state.dataUpdatedAt ||
+ (dehydratedQuery.promise &&
+ existingQuery.state.status !== 'pending' &&
+ existingQuery.state.fetchStatus !== 'fetching' &&
+ dehydratedQuery.dehydratedAt > existingQuery.state.dataUpdatedAt)
- if (hydrationIsNewer) {
- existingQueries.push(dehydratedQuery)
- }
+ if (hydrationIsNewer) {
+ existingQueries.push(dehydratedQuery)
}
}
+ }
- if (newQueries.length > 0) {
- // It's actually fine to call this with queries/state that already exists
- // in the cache, or is older. hydrate() is idempotent for queries.
- hydrate(client, { queries: newQueries }, optionsRef.current)
- }
- if (existingQueries.length > 0) {
- return existingQueries
- }
+ if (newQueries.length > 0) {
+ // It's actually fine to call this with queries/state that already exists
+ // in the cache, or is older. hydrate() is idempotent for queries.
+ hydrate(client, { queries: newQueries }, optionsRef.current)
}
- return undefined
+ return existingQueries.length > 0 ? existingQueries : undefined
}, [client, state])
useEffect(() => {
diff --git a/packages/preact-query/src/__tests__/HydrationBoundary.test.tsx b/packages/preact-query/src/__tests__/HydrationBoundary.test.tsx
index 48802de96c..b0487a5b30 100644
--- a/packages/preact-query/src/__tests__/HydrationBoundary.test.tsx
+++ b/packages/preact-query/src/__tests__/HydrationBoundary.test.tsx
@@ -322,106 +322,6 @@ describe('Preact hydration', () => {
})
})
- it('should not hydrate queries if state is null', async () => {
- const queryClient = new QueryClient()
-
- const hydrateSpy = vi.spyOn(coreModule, 'hydrate')
-
- function Page() {
- return null
- }
-
- render(
-
-
-
-
- ,
- )
-
- await Promise.all(
- Array.from({ length: 1000 }).map(async (_, index) => {
- await vi.advanceTimersByTimeAsync(index)
- expect(hydrateSpy).toHaveBeenCalledTimes(0)
- }),
- )
-
- hydrateSpy.mockRestore()
- queryClient.clear()
- })
-
- it('should not hydrate queries if state is undefined', async () => {
- const queryClient = new QueryClient()
-
- const hydrateSpy = vi.spyOn(coreModule, 'hydrate')
-
- function Page() {
- return null
- }
-
- render(
-
-
-
-
- ,
- )
-
- await vi.advanceTimersByTimeAsync(0)
- expect(hydrateSpy).toHaveBeenCalledTimes(0)
-
- hydrateSpy.mockRestore()
- queryClient.clear()
- })
-
- it('should not hydrate queries if state is not an object', async () => {
- const queryClient = new QueryClient()
-
- const hydrateSpy = vi.spyOn(coreModule, 'hydrate')
-
- function Page() {
- return null
- }
-
- render(
-
-
-
-
- ,
- )
-
- await vi.advanceTimersByTimeAsync(0)
- expect(hydrateSpy).toHaveBeenCalledTimes(0)
-
- hydrateSpy.mockRestore()
- queryClient.clear()
- })
-
- it('should handle state without queries property gracefully', async () => {
- const queryClient = new QueryClient()
-
- const hydrateSpy = vi.spyOn(coreModule, 'hydrate')
-
- function Page() {
- return null
- }
-
- render(
-
-
-
-
- ,
- )
-
- await vi.advanceTimersByTimeAsync(0)
- expect(hydrateSpy).toHaveBeenCalledTimes(0)
-
- hydrateSpy.mockRestore()
- queryClient.clear()
- })
-
// https://github.com/TanStack/query/issues/8677
it('should not infinite loop when hydrating promises that resolve to errors', async () => {
const originalHydrate = coreModule.hydrate
diff --git a/packages/react-query/src/HydrationBoundary.tsx b/packages/react-query/src/HydrationBoundary.tsx
index ce03eb40c8..66387e9f4a 100644
--- a/packages/react-query/src/HydrationBoundary.tsx
+++ b/packages/react-query/src/HydrationBoundary.tsx
@@ -17,7 +17,7 @@ export interface HydrationBoundaryProps {
/**
* The state to hydrate.
*/
- state: DehydratedState | null | undefined
+ state: DehydratedState
/**
* Optional. Note: unlike `hydrate`, `mutations` cannot be set here.
*/
@@ -113,52 +113,37 @@ export const HydrationBoundary = ({
// updating the UI.
const hydrationQueue: DehydratedState['queries'] | undefined =
React.useMemo(() => {
- if (state) {
- if (typeof state !== 'object') {
- return
- }
-
- const queryCache = client.getQueryCache()
- // State is supplied from the outside and we might as well fail
- // gracefully if it has the wrong shape, so while we type `queries`
- // as required, we still provide a fallback.
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
- const queries = state.queries || []
+ const queryCache = client.getQueryCache()
- const newQueries: DehydratedState['queries'] = []
- const existingQueries: DehydratedState['queries'] = []
- for (const dehydratedQuery of queries) {
- const existingQuery = queryCache.get(dehydratedQuery.queryHash)
+ const newQueries: DehydratedState['queries'] = []
+ const existingQueries: DehydratedState['queries'] = []
+ for (const dehydratedQuery of state.queries) {
+ const existingQuery = queryCache.get(dehydratedQuery.queryHash)
- if (!existingQuery) {
- newQueries.push(dehydratedQuery)
- } else {
- const hydrationIsNewer =
- dehydratedQuery.state.dataUpdatedAt >
- existingQuery.state.dataUpdatedAt ||
- (dehydratedQuery.promise &&
- existingQuery.state.status !== 'pending' &&
- existingQuery.state.fetchStatus !== 'fetching' &&
- dehydratedQuery.dehydratedAt >
- existingQuery.state.dataUpdatedAt)
+ if (!existingQuery) {
+ newQueries.push(dehydratedQuery)
+ } else {
+ const hydrationIsNewer =
+ dehydratedQuery.state.dataUpdatedAt >
+ existingQuery.state.dataUpdatedAt ||
+ (dehydratedQuery.promise &&
+ existingQuery.state.status !== 'pending' &&
+ existingQuery.state.fetchStatus !== 'fetching' &&
+ dehydratedQuery.dehydratedAt > existingQuery.state.dataUpdatedAt)
- if (hydrationIsNewer) {
- existingQueries.push(dehydratedQuery)
- }
+ if (hydrationIsNewer) {
+ existingQueries.push(dehydratedQuery)
}
}
+ }
- if (newQueries.length > 0) {
- // It's actually fine to call this with queries/state that already exists
- // in the cache, or is older. hydrate() is idempotent for queries.
- // eslint-disable-next-line react-hooks/refs
- hydrate(client, { queries: newQueries }, optionsRef.current)
- }
- if (existingQueries.length > 0) {
- return existingQueries
- }
+ if (newQueries.length > 0) {
+ // It's actually fine to call this with queries/state that already exists
+ // in the cache, or is older. hydrate() is idempotent for queries.
+ // eslint-disable-next-line react-hooks/refs
+ hydrate(client, { queries: newQueries }, optionsRef.current)
}
- return undefined
+ return existingQueries.length > 0 ? existingQueries : undefined
}, [client, state])
React.useEffect(() => {
diff --git a/packages/react-query/src/__tests__/HydrationBoundary.test.tsx b/packages/react-query/src/__tests__/HydrationBoundary.test.tsx
index 6418fb430f..ce8d809cc7 100644
--- a/packages/react-query/src/__tests__/HydrationBoundary.test.tsx
+++ b/packages/react-query/src/__tests__/HydrationBoundary.test.tsx
@@ -318,106 +318,6 @@ describe('React hydration', () => {
})
})
- it('should not hydrate queries if state is null', async () => {
- const queryClient = new QueryClient()
-
- const hydrateSpy = vi.spyOn(coreModule, 'hydrate')
-
- function Page() {
- return null
- }
-
- render(
-
-
-
-
- ,
- )
-
- await Promise.all(
- Array.from({ length: 1000 }).map(async (_, index) => {
- await vi.advanceTimersByTimeAsync(index)
- expect(hydrateSpy).toHaveBeenCalledTimes(0)
- }),
- )
-
- hydrateSpy.mockRestore()
- queryClient.clear()
- })
-
- it('should not hydrate queries if state is undefined', async () => {
- const queryClient = new QueryClient()
-
- const hydrateSpy = vi.spyOn(coreModule, 'hydrate')
-
- function Page() {
- return null
- }
-
- render(
-
-
-
-
- ,
- )
-
- await vi.advanceTimersByTimeAsync(0)
- expect(hydrateSpy).toHaveBeenCalledTimes(0)
-
- hydrateSpy.mockRestore()
- queryClient.clear()
- })
-
- it('should not hydrate queries if state is not an object', async () => {
- const queryClient = new QueryClient()
-
- const hydrateSpy = vi.spyOn(coreModule, 'hydrate')
-
- function Page() {
- return null
- }
-
- render(
-
-
-
-
- ,
- )
-
- await vi.advanceTimersByTimeAsync(0)
- expect(hydrateSpy).toHaveBeenCalledTimes(0)
-
- hydrateSpy.mockRestore()
- queryClient.clear()
- })
-
- it('should handle state without queries property gracefully', async () => {
- const queryClient = new QueryClient()
-
- const hydrateSpy = vi.spyOn(coreModule, 'hydrate')
-
- function Page() {
- return null
- }
-
- render(
-
-
-
-
- ,
- )
-
- await vi.advanceTimersByTimeAsync(0)
- expect(hydrateSpy).toHaveBeenCalledTimes(0)
-
- hydrateSpy.mockRestore()
- queryClient.clear()
- })
-
// https://github.com/TanStack/query/issues/8677
it('should not infinite loop when hydrating promises that resolve to errors', async () => {
const originalHydrate = coreModule.hydrate