Skip to content

Commit e61c784

Browse files
Merge branch 'main' into v5
# Conflicts: # docs/react/guides/caching.md # docs/react/guides/ssr.md # docs/svelte/ssr.md # examples/svelte/auto-refetching/src/routes/+page.svelte # examples/svelte/basic/src/lib/Post.svelte # examples/svelte/optimistic-updates-typescript/src/routes/+page.svelte # examples/svelte/playground/src/routes/Todos.svelte # examples/svelte/simple/src/lib/Simple.svelte # examples/svelte/ssr/src/lib/Post.svelte # package.json # packages/query-core/src/mutationObserver.ts # packages/react-query-devtools/package.json # packages/react-query-persist-client/package.json # packages/react-query/package.json # packages/react-query/src/__tests__/suspense.test.tsx # packages/react-query/src/__tests__/useMutation.test.tsx # packages/react-query/src/__tests__/useQueries.test.tsx # packages/svelte-query/src/__tests__/utils.ts # packages/svelte-query/src/createBaseQuery.ts # packages/svelte-query/src/createInfiniteQuery.ts # packages/svelte-query/src/createMutation.ts # packages/svelte-query/src/createQueries.ts # packages/svelte-query/src/createQuery.ts # packages/svelte-query/src/index.ts # packages/svelte-query/src/types.ts # packages/svelte-query/src/useHydrate.ts # packages/svelte-query/src/useIsFetching.ts # packages/svelte-query/src/useIsMutating.ts # packages/svelte-query/src/useQueryClient.ts # pnpm-lock.yaml
2 parents 2505f38 + 9404df4 commit e61c784

File tree

11 files changed

+209
-17
lines changed

11 files changed

+209
-17
lines changed

docs/react/guides/ssr.md

Lines changed: 182 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ The exact implementation of these mechanisms may vary from platform to platform,
1818
- Static Generation (SSG)
1919
- Server-side Rendering (SSR)
2020

21-
React Query supports both of these forms of pre-rendering regardless of what platform you may be using
21+
React Query supports both of these forms of pre-rendering regardless of what platform you may be using.
22+
23+
> Note: For notes about how to integrate with the new beta `/app`-folder in Next.js, see further down in this guide.
2224
2325
### Using `initialData`
2426

@@ -82,7 +84,7 @@ Now you are ready to prefetch some data in your pages with either [`getStaticPro
8284

8385
```tsx
8486
// pages/posts.jsx
85-
import { dehydrate, QueryClient, useQuery } from '@tanstack/react-query';
87+
import { dehydrate, QueryClient, useQuery } from '@tanstack/react-query'
8688

8789
export async function getStaticProps() {
8890
const queryClient = new QueryClient()
@@ -103,7 +105,10 @@ function Posts() {
103105

104106
// This query was not prefetched on the server and will not start
105107
// fetching until on the client, both patterns are fine to mix
106-
const { data: otherData } = useQuery({ queryKey: ['posts-2'], queryFn: getPosts })
108+
const { data: otherData } = useQuery({
109+
queryKey: ['posts-2'],
110+
queryFn: getPosts,
111+
})
107112

108113
// ...
109114
}
@@ -196,7 +201,7 @@ Now you are ready to prefetch some data in your [`loader`](https://remix.run/doc
196201

197202
```tsx
198203
// pages/posts.tsx
199-
import { dehydrate, QueryClient, useQuery } from '@tanstack/react-query';
204+
import { dehydrate, QueryClient, useQuery } from '@tanstack/react-query'
200205

201206
export async function loader() {
202207
const queryClient = new QueryClient()
@@ -213,7 +218,10 @@ function Posts() {
213218

214219
// This query was not prefetched on the server and will not start
215220
// fetching until on the client, both patterns are fine to mix
216-
const { data: otherData } = useQuery({ queryKey: ['posts-2'], queryFn: getPosts })
221+
const { data: otherData } = useQuery({
222+
queryKey: ['posts-2'],
223+
queryFn: getPosts,
224+
})
217225

218226
// ...
219227
}
@@ -241,7 +249,7 @@ This guide is at-best, a high level overview of how SSR with React Query should
241249
```tsx
242250
import { dehydrate, HydrationBoundary, QueryClient, QueryClientProvider } from '@tanstack/react-query';
243251

244-
async function handleRequest (req, res) {
252+
async function handleRequest(req, res) {
245253
const queryClient = new QueryClient()
246254
await queryClient.prefetchQuery(['key'], fn)
247255
const dehydratedState = dehydrate(queryClient)
@@ -288,40 +296,200 @@ ReactDOM.hydrate(
288296
<App />
289297
</HydrationBoundary>
290298
</QueryClientProvider>,
291-
document.getElementById('root')
299+
document.getElementById('root'),
292300
)
293301
```
294302

303+
## Using Experimental `app` Directory in Next.js 13
304+
305+
> **WARNING:** The `app` directory introduced in Next.js 13 is currently in beta, and it is not recommended for use in production. The API is not stable.
306+
>
307+
> This guide is provided as is to supply a quick start for early exploration of Next.js 13's experimental features and does not represent the final APIs.
308+
309+
Both prefetching approaches, using `initialData` or `<Hydrate>`, are available within the `app` directory.
310+
311+
- Prefetch the data in a Server Component and prop drill `initialData` to Client Components
312+
- Quick to set up for simple cases
313+
- May need to prop drill through multiple layers of Client Components
314+
- May need to prop drill to multiple Client Components using the same query
315+
- Query refetching is based on when the page loads instead of when the data was prefetched on the server
316+
- Prefetch the query on the server, dehydrate the cache and rehydrate it on the client with `<Hydrate>`
317+
- Requires slightly more setup up front
318+
- No need to prop drill
319+
- Query refetching is based on when the query was prefetched on the server
320+
321+
### `<QueryClientProvider>` is required by both the `initialData` and `<Hydrate>` prefetching approaches
322+
323+
The hooks provided by the `react-query` package need to retrieve a `QueryClient` from their context. Wrap your component tree with `<QueryClientProvider>` and pass it an instance of `QueryClient`.
324+
325+
```tsx
326+
// app/providers.jsx
327+
'use client'
328+
329+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
330+
331+
export default function Providers({ children }) {
332+
const [queryClient] = React.useState(() => new QueryClient())
333+
334+
return (
335+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
336+
)
337+
}
338+
```
339+
340+
```tsx
341+
// app/layout.jsx
342+
import Providers from './providers'
343+
344+
export default function RootLayout({ children }) {
345+
return (
346+
<html lang="en">
347+
<head />
348+
<body>
349+
<Providers>{children}</Providers>
350+
</body>
351+
</html>
352+
)
353+
}
354+
```
355+
356+
### Using `initialData`
357+
358+
Fetch your initial data in a Server Component higher up in the component tree, and pass it to your Client Component as a prop.
359+
360+
```tsx
361+
// app/page.jsx
362+
export default async function Home() {
363+
const initialData = await getPosts()
364+
365+
return <Posts posts={initialData} />
366+
}
367+
```
368+
369+
```tsx
370+
// app/posts.jsx
371+
'use client'
372+
373+
import { useQuery } from '@tanstack/react-query'
374+
375+
export function Posts(props) {
376+
const { data } = useQuery({
377+
queryKey: ['posts'],
378+
queryFn: getPosts,
379+
initialData: props.posts,
380+
})
381+
382+
// ...
383+
}
384+
```
385+
386+
### Using `<Hydrate>`
387+
388+
Create a request-scoped singleton instance of `QueryClient`. **This ensures that data is not shared between different users and requests, while still only creating the QueryClient once per request.**
389+
390+
```tsx
391+
// app/getQueryClient.jsx
392+
import { QueryClient } from '@tanstack/react-query'
393+
import { cache } from 'react'
394+
395+
const getQueryClient = cache(() => new QueryClient())
396+
export default getQueryClient
397+
```
398+
399+
Fetch your data in a Server Component higher up in the component tree than the Client Components that use the prefetched queries. Your prefetched queries will be available to all components deeper down the component tree.
400+
401+
- Retrieve the `QueryClient` singleton instance
402+
- Prefetch the data using the client's prefetchQuery method and wait for it to complete
403+
- Use `dehydrate` to obtain the dehydrated state of the prefetched queries from the query cache
404+
- Wrap the component tree that needs the prefetched queries inside `<Hydrate>`, and provide it with the dehydrated state
405+
- You can fetch inside multiple Server Components and use `<Hydrate>` in multiple places
406+
407+
> NOTE: TypeScript currently complains of a type error when using async Server Components. As a temporary workaround, use `{/* @ts-expect-error Server Component */}` when calling this component inside another. For more information, see [End-to-End Type Safety](https://beta.nextjs.org/docs/configuring/typescript#end-to-end-type-safety) in the Next.js 13 beta docs.
408+
409+
```tsx
410+
// app/hydratedPosts.jsx
411+
import { dehydrate, Hydrate } from '@tanstack/react-query'
412+
import getQueryClient from './getQueryClient'
413+
414+
export default async function HydratedPosts() {
415+
const queryClient = getQueryClient()
416+
await queryClient.prefetchQuery(['posts'], getPosts)
417+
const dehydratedState = dehydrate(queryClient)
418+
419+
return (
420+
<Hydrate state={dehydratedState}>
421+
<Posts />
422+
</Hydrate>
423+
)
424+
}
425+
```
426+
427+
During server rendering, calls to `useQuery` nested within the `<Hydrate>` Client Component will have access to prefetched data provided in the state property.
428+
429+
```tsx
430+
// app/posts.jsx
431+
'use client'
432+
433+
import { useQuery } from '@tanstack/react-query'
434+
435+
export default function Posts() {
436+
// This useQuery could just as well happen in some deeper child to
437+
// the "HydratedPosts"-component, data will be available immediately either way
438+
const { data } = useQuery({ queryKey: ['posts'], queryFn: getPosts })
439+
440+
// This query was not prefetched on the server and will not start
441+
// fetching until on the client, both patterns are fine to mix
442+
const { data: otherData } = useQuery({
443+
queryKey: ['posts-2'],
444+
queryFn: getPosts,
445+
})
446+
447+
// ...
448+
}
449+
```
450+
451+
As demonstrated, it's fine to prefetch some queries and let others fetch on the client. This means you can control what content server renders or not by adding or removing `prefetchQuery` for a specific query.
452+
453+
### Streaming, Suspense and server-side fetching
454+
455+
Right now, you always have to `await` the data in the Server Component. In the future, the goal is to be able to _start_ prefetching in a Server Component but not block rendering, instead streaming markup and data to the client incrementally as it gets available. This is currently lacking support in both React and Query.
456+
457+
Similarily, you _must_ currently prefetch the data in a Server Component if you want it to be server rendered. A `useQuery()` call even with the `suspense` option enabled will not fetch data on the server, only on the client. We hope to support this in the future, but exact details are still unknown.
458+
295459
## Custom SSR with suspense
296460

297461
If you do not want to provide `prefetchQuery()` for all your queries in the SSR you can use suspense.
298462

299463
### Server
300464

301465
```tsx
302-
import { dehydrate, QueryClient, QueryClientProvider } from '@tanstack/react-query';
466+
import {
467+
dehydrate,
468+
QueryClient,
469+
QueryClientProvider,
470+
} from '@tanstack/react-query'
303471
import ssrPrepass from 'react-ssr-prepass'
304472

305-
async function handleRequest (req, res) {
473+
async function handleRequest(req, res) {
306474
const queryClient = new QueryClient()
307475

308476
// React SSR does not support ErrorBoundary
309477
try {
310478
// Traverse the tree and fetch all Suspense data (thrown promises)
311-
await ssrPrepass(<App />);
479+
await ssrPrepass(<App />)
312480
} catch (e) {
313-
console.error(e);
481+
console.error(e)
314482
// Send the index.html (without SSR) on error, so user can try to recover and see something
315-
return res.sendFile('path/to/dist/index.html');
483+
return res.sendFile('path/to/dist/index.html')
316484
}
317485

318486
const html = ReactDOM.renderToString(
319487
<QueryClientProvider client={queryClient}>
320488
<App />
321-
</QueryClientProvider>
489+
</QueryClientProvider>,
322490
)
323491

324-
const dehydratedState = dehydrate(queryClient);
492+
const dehydratedState = dehydrate(queryClient)
325493

326494
res.send(`
327495
<html>

docs/svelte/ssr.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export const load: PageLoad = async () => {
6262
const query = createQuery({
6363
queryKey: ['posts'],
6464
queryFn: getPosts,
65-
initialData: posts
65+
initialData: data.posts
6666
})
6767
</script>
6868
```
@@ -107,9 +107,9 @@ export const load: LayoutLoad = async () => {
107107
```markdown
108108
<script lang="ts">
109109
import { QueryClientProvider } from '@tanstack/svelte-query'
110-
import type { PageData } from './$types'
110+
import type { LayoutData } from './$types'
111111

112-
export let data: PageData
112+
export let data: LayoutData
113113
</script>
114114

115115
<QueryClientProvider client={data.queryClient}>

packages/query-core/src/tests/mutations.test.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,4 +358,20 @@ describe('mutations', () => {
358358
expect(onSuccess).not.toHaveBeenCalled()
359359
expect(onSettled).not.toHaveBeenCalled()
360360
})
361+
362+
test('mutate update the mutation state even without an active subscription', async () => {
363+
const onSuccess = jest.fn()
364+
const onSettled = jest.fn()
365+
366+
const mutation = new MutationObserver(queryClient, {
367+
mutationFn: async () => {
368+
return 'update'
369+
},
370+
})
371+
372+
await mutation.mutate(undefined, { onSuccess, onSettled })
373+
expect(mutation.getCurrentResult().data).toEqual('update')
374+
expect(onSuccess).not.toHaveBeenCalled()
375+
expect(onSettled).not.toHaveBeenCalled()
376+
})
361377
})

packages/react-query-devtools/src/Explorer.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
'use client'
12
import * as React from 'react'
23

34
import { displayValue, styled } from './utils'

packages/react-query-devtools/src/devtools.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
'use client'
12
import * as React from 'react'
23
import type {
34
QueryCache,

packages/react-query-devtools/src/theme.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
'use client'
12
import * as React from 'react'
23

34
export const defaultTheme = {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
'use client'
12
import * as React from 'react'
23

34
import type { PersistQueryClientOptions } from '@tanstack/query-persist-client-core'

packages/react-query/src/HydrationBoundary.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
'use client'
12
import * as React from 'react'
23

34
import type { HydrateOptions, QueryClient } from '@tanstack/query-core'

packages/react-query/src/QueryClientProvider.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
'use client'
12
import * as React from 'react'
23

34
import type { QueryClient } from '@tanstack/query-core'

packages/react-query/src/QueryErrorResetBoundary.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
'use client'
12
import * as React from 'react'
23

34
// CONTEXT

0 commit comments

Comments
 (0)