Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions docs/framework/react/guides/advanced-ssr.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ The first step of any React Query setup is always to create a `queryClient` and

// Since QueryClientProvider relies on useContext under the hood, we have to put 'use client' on top
import {
isServer,
environmentManager,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
Expand All @@ -51,7 +51,7 @@ function makeQueryClient() {
let browserQueryClient: QueryClient | undefined = undefined

function getQueryClient() {
if (isServer) {
if (environmentManager.isServer()) {
// Server: always make a new query client
return makeQueryClient()
} else {
Expand Down Expand Up @@ -376,7 +376,7 @@ We will also need to move the `getQueryClient()` function out of our `app/provid
```tsx
// app/get-query-client.ts
import {
isServer,
environmentManager,
QueryClient,
defaultShouldDehydrateQuery,
} from '@tanstack/react-query'
Expand Down Expand Up @@ -408,7 +408,7 @@ function makeQueryClient() {
let browserQueryClient: QueryClient | undefined = undefined

export function getQueryClient() {
if (isServer) {
if (environmentManager.isServer()) {
// Server: always make a new query client
return makeQueryClient()
} else {
Expand Down Expand Up @@ -564,7 +564,7 @@ To achieve this, wrap your app in the `ReactQueryStreamedHydration` component:
'use client'

import {
isServer,
environmentManager,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
Expand All @@ -586,7 +586,7 @@ function makeQueryClient() {
let browserQueryClient: QueryClient | undefined = undefined

function getQueryClient() {
if (isServer) {
if (environmentManager.isServer()) {
// Server: always make a new query client
return makeQueryClient()
} else {
Expand Down
4 changes: 2 additions & 2 deletions docs/framework/react/guides/suspense.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ To achieve this, wrap your app in the `ReactQueryStreamedHydration` component:
'use client'

import {
isServer,
environmentManager,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
Expand All @@ -142,7 +142,7 @@ function makeQueryClient() {
let browserQueryClient: QueryClient | undefined = undefined

function getQueryClient() {
if (isServer) {
if (environmentManager.isServer()) {
// Server: always make a new query client
return makeQueryClient()
} else {
Expand Down
86 changes: 86 additions & 0 deletions packages/query-core/src/__tests__/utils.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { describe, expect, it, vi } from 'vitest'
import { queryKey } from '@tanstack/query-test-utils'
import { QueryClient } from '..'
import {
addConsumeAwareSignal,
addToEnd,
addToStart,
ensureQueryFn,
Expand Down Expand Up @@ -420,6 +421,29 @@ describe('core/utils', () => {

expect(next).toBe(current)
})

it('should stop structural sharing once the recursion depth exceeds the limit', () => {
const nest = (depth: number, leaf: number) => {
let value: any = { leaf }
for (let i = 0; i < depth; i++) {
value = { child: value }
}
return value
}

const prev = nest(502, 1)
const next = nest(502, 2)
const result = replaceEqualDeep(prev, next)

let resultNode = result
let nextNode = next
for (let i = 0; i < 502; i++) {
resultNode = resultNode.child
nextNode = nextNode.child
}

expect(resultNode).toBe(nextNode)
})
})

describe('matchMutation', () => {
Expand Down Expand Up @@ -603,4 +627,66 @@ describe('core/utils', () => {
expect(shouldThrowError(undefined, [new Error('test error')])).toBe(false)
})
})

describe('addConsumeAwareSignal', () => {
it('should expose the signal on the query context while preserving its properties', () => {
const controller = new AbortController()
const key = queryKey()
const context = addConsumeAwareSignal(
{ queryKey: key, meta: undefined },
() => controller.signal,
vi.fn(),
)

expect(context.queryKey).toBe(key)
expect(context.signal).toBe(controller.signal)
})

it('should call onCancelled immediately when the signal is already aborted on first access', () => {
const controller = new AbortController()
controller.abort()
const onCancelled = vi.fn()
const object = addConsumeAwareSignal(
{},
() => controller.signal,
onCancelled,
)

// Access the signal to consume it
void object.signal

expect(onCancelled).toHaveBeenCalledTimes(1)
})

it('should flag cancellation when the consumed signal aborts, mirroring streamed/infinite queries', () => {
const controller = new AbortController()
let cancelled = false
const context = addConsumeAwareSignal(
{ queryKey: queryKey() },
() => controller.signal,
() => (cancelled = true),
)

void context.signal
expect(cancelled).toBe(false)

controller.abort()
expect(cancelled).toBe(true)
})

it('should consume the signal only once across repeated accesses', () => {
const controller = new AbortController()
const addEventListener = vi.spyOn(controller.signal, 'addEventListener')
const context = addConsumeAwareSignal(
{ queryKey: queryKey() },
() => controller.signal,
vi.fn(),
)

expect(context.signal).toBe(controller.signal)
expect(context.signal).toBe(controller.signal)

expect(addEventListener).toHaveBeenCalledTimes(1)
})
})
})
Loading