Skip to content

Commit

Permalink
Merge branch 'main' into usePrefetchQuery
Browse files Browse the repository at this point in the history
  • Loading branch information
TkDodo committed Jun 2, 2024
2 parents 9c8dc74 + 3a66139 commit af072f6
Show file tree
Hide file tree
Showing 139 changed files with 5,685 additions and 3,595 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,5 @@ jobs:
TAG: ${{ inputs.tag }}
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
directory: packages
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
4 changes: 2 additions & 2 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@ jobs:
run: npx nx-cloud stop-all-agents
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
directory: packages
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
6 changes: 5 additions & 1 deletion docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -849,9 +849,13 @@
"to": "framework/react/examples/rick-morty"
},
{
"label": "Next.js",
"label": "Next.js Pages",
"to": "framework/react/examples/nextjs"
},
{
"label": "Next.js app with prefetching",
"to": "framework/react/examples/nextjs-app-prefetching"
},
{
"label": "Next.js app with streaming",
"to": "framework/react/examples/nextjs-suspense-streaming"
Expand Down
6 changes: 6 additions & 0 deletions docs/framework/react/community/community-projects.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ A library for creating typesafe standardized query keys, useful for cache manage

Link: https://github.com/lukemorales/query-key-factory

## Rapini

🥬 OpenAPI to React Query (or SWR) & Axios

Link: https://github.com/rametta/rapini

## React Query Kit

🕊️ A toolkit for ReactQuery that makes ReactQuery hooks reusable and typesafe
Expand Down
81 changes: 76 additions & 5 deletions docs/framework/react/guides/advanced-ssr.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function getQueryClient() {
return makeQueryClient()
} else {
// Browser: make a new query client if we don't already have one
// This is very important so we don't re-make a new client if React
// This is very important, so we don't re-make a new client if React
// suspends during the initial render. This may not be needed if we
// have a suspense boundary BELOW the creation of the query client
if (!browserQueryClient) browserQueryClient = makeQueryClient()
Expand Down Expand Up @@ -354,9 +354,80 @@ The Next.js app router automatically streams any part of the application that is

With the prefetching patterns described above, React Query is perfectly compatible with this form of streaming. As the data for each Suspense boundary resolves, Next.js can render and stream the finished content to the browser. This works even if you are using `useQuery` as outlined above because the suspending actually happens when you `await` the prefetch.

Note that right now, you have to await all prefetches for this to work. This means all prefetches are considered critical content and will block that Suspense boundary.
As of React Query v5.40.0, you don't have to `await` all prefetches for this to work, as `pending` Queries can also be dehydrated and sent to the client. This lets you kick off prefetches as early as possible without letting them block an entire Suspense boundary, and streams the _data_ to the client as the query finishes. This can be useful for example if you want to prefetch some content that is only visible after some user interaction, or say if you want to `await` and render the first page of an infinite query, but start prefetching page 2 without blocking rendering.

As an aside, in the future it might be possible to skip the await for "optional" prefetches that are not critical for this Suspense boundary. This would let you kick off prefetches as early as possible without letting them block an entire Suspense boundary, and streaming the _data_ to the client as the query finishes. This could be useful for example if you want to prefetch some content that is only visible after some user interaction, or say if you want to await and render the first page of an infinite query, but start prefetching page 2 without blocking rendering.
To make this work, we have to instruct the `queryClient` to also `dehydrate` pending Queries. We can do this globally, or by passing that option directly to `hydrate`:

```tsx
// app/get-query-client.ts
import { QueryClient, defaultShouldDehydrateQuery } from '@tanstack/react-query'

function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
},
dehydrate: {
// per default, only successful Queries are included,
// this includes pending Queries as well
shouldDehydrateQuery: (query) =>
defaultShouldDehydrateQuery(query) ||
query.state.status === 'pending',
},
},
})
}
```

> Note: This works in NextJs and Server Components because React can serialize Promises over the wire when you pass them down to Client Components.
Then, all we need to do is provide a `HydrationBoundary`, but we don't need to `await` prefetches anymore:

```tsx
// app/posts/page.jsx
import {
dehydrate,
HydrationBoundary,
QueryClient,
} from '@tanstack/react-query'
import { getQueryClient } from './get-query-client'
import Posts from './posts'

// the function doesn't need to be `async` because we don't `await` anything
export default function PostsPage() {
const queryClient = getQueryClient()

// look ma, no await
queryClient.prefetchQuery({
queryKey: ['posts'],
queryFn: getPosts,
})

return (
<HydrationBoundary state={dehydrate(queryClient)}>
<Posts />
</HydrationBoundary>
)
}
```

On the client, the Promise will be put into the QueryCache for us. That means we can now call `useSuspenseQuery` inside the `Posts` component to "use" that Promise (which was created on the Server):

```tsx
// app/posts/posts.tsx
'use client'

export default function Posts() {
const { data } = useSuspenseQuery({ queryKey: ['posts'], queryFn: getPosts })

// ...
}
```

> Note that you could also `useQuery` instead of `useSuspenseQuery`, and the Promise would still be picked up correctly. However, NextJs won't suspend in that case and the component will render in the `pending` status, which also opts out of server rendering the content.
For more information, check out the [Next.js App with Prefetching Example](../../examples/nextjs-app-prefetching).

## Experimental streaming without prefetching in Next.js

Expand Down Expand Up @@ -394,8 +465,8 @@ function getQueryClient() {
return makeQueryClient()
} else {
// Browser: make a new query client if we don't already have one
// This is very important so we don't re-make a new client if React
// supsends during the initial render. This may not be needed if we
// This is very important, so we don't re-make a new client if React
// suspends during the initial render. This may not be needed if we
// have a suspense boundary BELOW the creation of the query client
if (!browserQueryClient) browserQueryClient = makeQueryClient()
return browserQueryClient
Expand Down
8 changes: 4 additions & 4 deletions docs/framework/react/guides/suspense.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ Since you can't change `throwOnError` (because it would allow for `data` to beco
```tsx
import { useSuspenseQuery } from '@tanstack/react-query'

const { data, error } = useSuspenseQuery({ queryKey, queryFn })
const { data, error, isFetching } = useSuspenseQuery({ queryKey, queryFn })

if (error) {
if (error && !isFetching) {
throw error
}

Expand Down Expand Up @@ -142,8 +142,8 @@ function getQueryClient() {
return makeQueryClient()
} else {
// Browser: make a new query client if we don't already have one
// This is very important so we don't re-make a new client if React
// supsends during the initial render. This may not be needed if we
// This is very important, so we don't re-make a new client if React
// suspends during the initial render. This may not be needed if we
// have a suspense boundary BELOW the creation of the query client
if (!browserQueryClient) browserQueryClient = makeQueryClient()
return browserQueryClient
Expand Down
6 changes: 4 additions & 2 deletions docs/framework/react/reference/hydration.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const dehydratedState = dehydrate(queryClient, {
- You **should not** rely on the exact format of this response, it is not part of the public API and can change at any time
- This result is not in serialized form, you need to do that yourself if desired

### limitations
### Limitations

Some storage systems (such as browser [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API)) require values to be JSON serializable. If you need to dehydrate values that are not automatically serializable to JSON (like `Error` or `undefined`), you have to serialize them for yourself. Since only successful queries are included per default, to also include `Errors`, you have to provide `shouldDehydrateQuery`, e.g.:

Expand Down Expand Up @@ -88,7 +88,7 @@ hydrate(queryClient, dehydratedState, options)

### Limitations

If the queries included in dehydration already exist in the queryCache, `hydrate` does not overwrite them and they will be **silently** discarded.
If the queries you're trying to hydrate already exist in the queryCache, `hydrate` will only overwrite them if the data is newer than the data present in the cache. Otherwise, it will **not** get applied.

[//]: # 'HydrationBoundary'

Expand All @@ -104,6 +104,8 @@ function App() {
}
```

> Note: Only `queries` can be dehydrated with an `HydrationBoundary`.
**Options**

- `state: DehydratedState`
Expand Down
1 change: 1 addition & 0 deletions docs/reference/QueryClient.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Its available methods are:
- `defaultOptions?: DefaultOptions`
- Optional
- Define defaults for all queries and mutations using this queryClient.
- You can also define defaults to be used for [hydration](../../framework/react/reference/hydration.md)

## `queryClient.fetchQuery`

Expand Down
23 changes: 10 additions & 13 deletions examples/angular/basic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,21 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^17.3.9",
"@angular/common": "^17.3.9",
"@angular/compiler": "^17.3.9",
"@angular/core": "^17.3.9",
"@angular/forms": "^17.3.9",
"@angular/platform-browser": "^17.3.9",
"@angular/platform-browser-dynamic": "^17.3.9",
"@angular/router": "^17.3.9",
"@tanstack/angular-query-experimental": "^5.37.1",
"@angular/common": "^17.3.10",
"@angular/compiler": "^17.3.10",
"@angular/core": "^17.3.10",
"@angular/platform-browser": "^17.3.10",
"@angular/platform-browser-dynamic": "^17.3.10",
"@tanstack/angular-query-experimental": "^5.40.0",
"rxjs": "^7.8.1",
"tslib": "^2.6.2",
"zone.js": "^0.14.6"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.3.7",
"@angular/cli": "^17.3.7",
"@angular/compiler-cli": "^17.3.9",
"@tanstack/angular-query-devtools-experimental": "^5.37.1",
"@angular-devkit/build-angular": "^17.3.8",
"@angular/cli": "^17.3.8",
"@angular/compiler-cli": "^17.3.10",
"@tanstack/angular-query-devtools-experimental": "^5.40.0",
"typescript": "5.3.3"
}
}
23 changes: 10 additions & 13 deletions examples/angular/infinite-query-with-max-pages/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,21 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^17.3.9",
"@angular/common": "^17.3.9",
"@angular/compiler": "^17.3.9",
"@angular/core": "^17.3.9",
"@angular/forms": "^17.3.9",
"@angular/platform-browser": "^17.3.9",
"@angular/platform-browser-dynamic": "^17.3.9",
"@angular/router": "^17.3.9",
"@tanstack/angular-query-experimental": "^5.37.1",
"@angular/common": "^17.3.10",
"@angular/compiler": "^17.3.10",
"@angular/core": "^17.3.10",
"@angular/platform-browser": "^17.3.10",
"@angular/platform-browser-dynamic": "^17.3.10",
"@tanstack/angular-query-experimental": "^5.40.0",
"rxjs": "^7.8.1",
"tslib": "^2.6.2",
"zone.js": "^0.14.6"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.3.7",
"@angular/cli": "^17.3.7",
"@angular/compiler-cli": "^17.3.9",
"@tanstack/angular-query-devtools-experimental": "^5.37.1",
"@angular-devkit/build-angular": "^17.3.8",
"@angular/cli": "^17.3.8",
"@angular/compiler-cli": "^17.3.10",
"@tanstack/angular-query-devtools-experimental": "^5.40.0",
"typescript": "5.3.3"
}
}
24 changes: 11 additions & 13 deletions examples/angular/router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,22 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^17.3.9",
"@angular/common": "^17.3.9",
"@angular/compiler": "^17.3.9",
"@angular/core": "^17.3.9",
"@angular/forms": "^17.3.9",
"@angular/platform-browser": "^17.3.9",
"@angular/platform-browser-dynamic": "^17.3.9",
"@angular/router": "^17.3.9",
"@tanstack/angular-query-experimental": "^5.37.1",
"@angular/common": "^17.3.10",
"@angular/compiler": "^17.3.10",
"@angular/core": "^17.3.10",
"@angular/platform-browser": "^17.3.10",
"@angular/platform-browser-dynamic": "^17.3.10",
"@angular/router": "^17.3.10",
"@tanstack/angular-query-experimental": "^5.40.0",
"rxjs": "^7.8.1",
"tslib": "^2.6.2",
"zone.js": "^0.14.6"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.3.7",
"@angular/cli": "^17.3.7",
"@angular/compiler-cli": "^17.3.9",
"@tanstack/angular-query-devtools-experimental": "^5.37.1",
"@angular-devkit/build-angular": "^17.3.8",
"@angular/cli": "^17.3.8",
"@angular/compiler-cli": "^17.3.10",
"@tanstack/angular-query-devtools-experimental": "^5.40.0",
"typescript": "5.3.3"
}
}
24 changes: 11 additions & 13 deletions examples/angular/simple/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,22 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^17.3.9",
"@angular/common": "^17.3.9",
"@angular/compiler": "^17.3.9",
"@angular/core": "^17.3.9",
"@angular/forms": "^17.3.9",
"@angular/platform-browser": "^17.3.9",
"@angular/platform-browser-dynamic": "^17.3.9",
"@angular/router": "^17.3.9",
"@tanstack/angular-query-experimental": "^5.37.1",
"@angular/common": "^17.3.10",
"@angular/compiler": "^17.3.10",
"@angular/core": "^17.3.10",
"@angular/platform-browser": "^17.3.10",
"@angular/platform-browser-dynamic": "^17.3.10",
"@angular/router": "^17.3.10",
"@tanstack/angular-query-experimental": "^5.40.0",
"rxjs": "^7.8.1",
"tslib": "^2.6.2",
"zone.js": "^0.14.6"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.3.7",
"@angular/cli": "^17.3.7",
"@angular/compiler-cli": "^17.3.9",
"@tanstack/angular-query-devtools-experimental": "^5.37.1",
"@angular-devkit/build-angular": "^17.3.8",
"@angular/cli": "^17.3.8",
"@angular/compiler-cli": "^17.3.10",
"@tanstack/angular-query-devtools-experimental": "^5.40.0",
"typescript": "5.3.3"
}
}
8 changes: 4 additions & 4 deletions examples/react/algolia/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
"dependencies": {
"@algolia/client-search": "4.23.3",
"@algolia/transporter": "4.23.3",
"@tanstack/react-query": "^5.37.1",
"@tanstack/react-query-devtools": "^5.37.1",
"@tanstack/react-query": "^5.40.0",
"@tanstack/react-query-devtools": "^5.40.0",
"algoliasearch": "4.23.3",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react": "19.0.0-rc-4c2e457c7c-20240522",
"react-dom": "19.0.0-rc-4c2e457c7c-20240522"
},
"devDependencies": {
"@tanstack/eslint-plugin-query": "^5.35.6",
Expand Down
4 changes: 2 additions & 2 deletions examples/react/auto-refetching/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"start": "next start"
},
"dependencies": {
"@tanstack/react-query": "^5.37.1",
"@tanstack/react-query-devtools": "^5.37.1",
"@tanstack/react-query": "^5.40.0",
"@tanstack/react-query-devtools": "^5.40.0",
"axios": "^1.6.8",
"isomorphic-unfetch": "4.0.2",
"next": "^14.0.0",
Expand Down
8 changes: 4 additions & 4 deletions examples/react/basic-graphql-request/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
"preview": "vite preview"
},
"dependencies": {
"@tanstack/react-query": "^5.37.1",
"@tanstack/react-query-devtools": "^5.37.1",
"@tanstack/react-query": "^5.40.0",
"@tanstack/react-query-devtools": "^5.40.0",
"graphql": "^16.8.1",
"graphql-request": "^7.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react": "19.0.0-rc-4c2e457c7c-20240522",
"react-dom": "19.0.0-rc-4c2e457c7c-20240522"
},
"devDependencies": {
"@vitejs/plugin-react": "^4.2.1",
Expand Down

0 comments on commit af072f6

Please sign in to comment.