Skip to content

Commit

Permalink
docs(router): update deferred data loading guide when using external …
Browse files Browse the repository at this point in the history
…libraries (#2940)

* docs(router): update deferred data loading guide when using external libraries
  • Loading branch information
SeanCassiere authored Dec 6, 2024
1 parent 8311780 commit cebb221
Showing 1 changed file with 61 additions and 3 deletions.
64 changes: 61 additions & 3 deletions docs/framework/react/guide/deferred-data-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ TanStack Router is designed to run loaders in parallel and wait for all of them

Deferred data loading is a pattern that allows the router to render the next location's critical data/markup while slower, non-critical route data is resolved in the background. This process works on both the client and server (via streaming) and is a great way to improve the perceived performance of your application.

If you are using a library like [TanStack Query](https://react-query.tanstack.com) or any other data fetching library, then deferred data loading works a bit differently. Skip ahead to the [Deferred Data Loading with External Libraries](#deferred-data-loading-with-external-libraries) section for more information.

## Deferred Data Loading with `defer` and `Await`

To defer slow or non-critical data, wrap an **unawaited/unresolved** promise in the `defer` function and return it anywhere in your loader response:

```tsx
// src/routes/posts.$postId.tsx

import * as React from 'react'
import { createFileRoute, defer } from '@tanstack/react-router'

Expand All @@ -40,7 +41,6 @@ In the component, deferred promises can be resolved and utilized using the `Awai

```tsx
// src/routes/posts.$postId.tsx

import * as React from 'react'
import { createFileRoute, Await } from '@tanstack/react-router'

Expand All @@ -50,7 +50,9 @@ export const Route = createFileRoute('/posts/$postId')({
})

function PostIdComponent() {
const { deferredSlowData } = Route.useLoaderData()
const { deferredSlowData, fastData } = Route.useLoaderData()

// do something with fastData

return (
<Await promise={deferredSlowData} fallback={<div>Loading...</div>}>
Expand All @@ -69,6 +71,62 @@ The `Await` component resolves the promise by triggering the nearest suspense bo

If the promise is rejected, the `Await` component will throw the serialized error, which can be caught by the nearest error boundary.

## Deferred Data Loading with External libraries

When your strategy for fetching information for the route relies on [External Data Loading](./external-data-loading.md) with an external library like [TanStack Query](https://react-query.tanstack.com), deferred data loading works a bit differently, as the library handles the data fetching and caching for you outside of TanStack Router.

So, instead of using `defer` and `Await`, you'll instead want to use the Route's `loader` to kick off the data fetching and then use the library's hooks to access the data in your components.

```tsx
// src/routes/posts.$postId.tsx
import * as React from 'react'
import { createFileRoute } from '@tanstack/react-router'
import { slowDataOptions, fastDataOptions } from '~/api/query-options'

export const Route = createFileRoute('/posts/$postId')({
loader: async ({ context: { queryClient } }) => {
// Kick off the fetching of some slower data, but do not await it
queryClient.prefetchQuery(slowDataOptions())

// Fetch and await some data that resolves quickly
await queryClient.ensureQueryData(fastDataOptions())
},
})
```

Then in your component, you can use the library's hooks to access the data:

```tsx
// src/routes/posts.$postId.tsx
import * as React from 'react'
import { createFileRoute } from '@tanstack/react-router'
import { useSuspenseQuery } from '@tanstack/react-query'
import { slowDataOptions, fastDataOptions } from '~/api/query-options'

export const Route = createFileRoute('/posts/$postId')({
// ...
component: PostIdComponent,
})

function PostIdComponent() {
const fastData = useSuspenseQuery(fastDataOptions())

// do something with fastData

return (
<React.Suspense fallback={<div>Loading...</div>}>
<SlowDataComponent />
</React.Suspense>
)
}

function SlowDataComponent() {
const data = useSuspenseQuery(slowDataOptions())

return <div>{data}</div>
}
```

## Caching and Invalidation

Streamed promises follow the same lifecycle as the loader data they are associated with. They can even be preloaded!
Expand Down

0 comments on commit cebb221

Please sign in to comment.