Skip to content

Commit

Permalink
fix: signal passthrough, docs
Browse files Browse the repository at this point in the history
  • Loading branch information
tannerlinsley committed Feb 6, 2023
1 parent cc6de27 commit 7734419
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 21 deletions.
60 changes: 44 additions & 16 deletions docs/guide/data-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ Here is a simple example of using `onLoad` to fetch data for a route:
import { Route } from '@tanstack/react-router'
import { Loader, useLoaderInstance } from '@tanstack/react-loaders'

// Create a loader
const postsLoader = new Loader({
key: 'posts',
loader: async (params) => {
Expand All @@ -73,7 +72,6 @@ const postsLoader = new Loader({
},
})

// Create a route
const postsRoute = new Route({
getParentPath: () => rootRoute,
path: 'posts',
Expand Down Expand Up @@ -112,7 +110,6 @@ The `params` property of the `onLoad` function is an object containing the route
import { Route } from '@tanstack/react-router'
import { Loader, useLoaderInstance } from '@tanstack/react-loaders'

// Create a loader
const postLoader = new Loader({
key: 'post',
// Accept a postId string variable
Expand All @@ -123,7 +120,6 @@ const postLoader = new Loader({
},
})

// Create a route
const postRoute = new Route({
getParentPath: () => postsRoute,
path: '$postId',
Expand All @@ -147,7 +143,6 @@ The `search` and `routeSearch` properties of the `onLoad` function are objects c
import { Route } from '@tanstack/react-router'
import { Loader, useLoaderInstance } from '@tanstack/react-loaders'

// Create a loader
const postsLoader = new Loader({
key: 'posts',
// Accept a page number variable
Expand All @@ -158,7 +153,6 @@ const postsLoader = new Loader({
},
})

// Create a route
const postsRoute = new Route({
getParentPath: () => rootRoute,
path: 'posts',
Expand Down Expand Up @@ -190,7 +184,6 @@ The `context` and `routeContext` properties of the `onLoad` function are objects
import { Route } from '@tanstack/react-router'
import { Loader, useLoaderInstance } from '@tanstack/react-loaders'

// Create a loader
const postsLoader = new Loader({
key: 'posts',
loader: async () => {
Expand Down Expand Up @@ -241,32 +234,67 @@ const router = new ReactRouter({

## Using the Abort Signal

The `signal` property of the `onLoad` function is an [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) which is cancelled when the route is unloaded or when the `onLoad` call becomes outdated. This is useful for cancelling network requests when the route is unloaded or when the route's params change.
The `signal` property of the `onLoad` function is an [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) which is cancelled when the route is unloaded or when the `onLoad` call becomes outdated. This is useful for cancelling network requests when the route is unloaded or when the route's params change. Here is an example using TanStack Loader's signal passthrough:

```tsx
import { Route } from '@tanstack/react-router'
import { Loader, useLoaderInstance } from '@tanstack/react-loaders'

// Create a loader
const postsLoader = new Loader({
key: 'posts',
// Accept a page number variable
loader: async (pageIndex: number, signal: AbortSignal) => {
loader: async (pageIndex: number, { signal }) => {
const res = await fetch(`/api/posts?page=${pageIndex}`, { signal })
if (!res.ok) throw new Error('Failed to fetch posts')
return res.json()
},
})

##
const postsRoute = new Route({
getParentPath: () => rootRoute,
path: 'posts',
async onLoad({ signal }) {
// Pass the route's signal to the loader
await postsLoader.load({ signal })
},
component: () => {
const posts = useLoaderInstance({ loader: postsLoader })

##
return <div>...</div>
},
})
```

##
## Using the `prefetch` flag

##
The `prefetch` property of the `onLoad` function is a boolean which is `true` when the route is being loaded via a prefetch action. Some data loading libraries may handle prefetching differently than a standard fetch, so you may want to pass `prefetch` to your data loading library, or use it to execute the appropriate data loading logic. Here is an example using TanStack Loader and it's built-in `prefetch` flag:

##
```tsx
import { Route } from '@tanstack/react-router'
import { Loader, useLoaderInstance } from '@tanstack/react-loaders'

##
const postsLoader = new Loader({
key: 'posts',
loader: async () => {
const res = await fetch(`/api/posts?page=${pageIndex}`)
if (!res.ok) throw new Error('Failed to fetch posts')
return res.json()
},
})

const postsRoute = new Route({
getParentPath: () => rootRoute,
path: 'posts',
async onLoad({ prefetch }) {
// Pass the route's prefetch to the loader
await postsLoader.load({ prefetch })
},
component: () => {
const posts = useLoaderInstance({ loader: postsLoader })

return <div>...</div>
},
})
```

> 🧠 TanStack Loaders uses the `prefetch` flag to determine cache freshness vs non-prefetch calls and also to determine if the global `isLoading` or `isPrefetching` flags should be incremented or not.
18 changes: 15 additions & 3 deletions packages/loaders/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ export interface LoaderClientOptions<
loaderInstance: LoaderInstance<TKey, TVariables, TData, TError>,
) => (
variables: TVariables,
loaderInstance: LoaderInstance<TKey, TVariables, TData, TError>,
loaderOpts: {
loaderInstance: LoaderInstance<TKey, TVariables, TData, TError>
signal?: AbortSignal
},
) => Promise<TData>
}

Expand Down Expand Up @@ -235,7 +238,10 @@ interface LoaderOptions<
gcMaxAge?: number
loader: (
variables: TVariables,
Loader: LoaderInstance<TKey, TVariables, TData, TError>,
loaderOpts: {
loaderInstance: LoaderInstance<TKey, TVariables, TData, TError>
signal?: AbortSignal
},
) => TData | Promise<TData>
onAllInvalidate?: LoaderCallback<TKey, TVariables, TData, TError>
onEachInvalidate?: LoaderInstanceCallback<TKey, TVariables, TData, TError>
Expand Down Expand Up @@ -365,6 +371,7 @@ export class Loader<
{
maxAge?: number
preload?: boolean
signal?: AbortSignal
}
> = async (opts: any = {}) => {
return this.getInstance(opts).load(opts as any)
Expand Down Expand Up @@ -543,6 +550,7 @@ export class LoaderInstance<
maxAge?: number
preload?: boolean
isFocusReload?: boolean
signal?: AbortSignal
}): Promise<TData> => {
if (
opts?.isFocusReload &&
Expand Down Expand Up @@ -611,6 +619,7 @@ export class LoaderInstance<
fetch = async (opts?: {
maxAge?: number
preload?: boolean
signal?: AbortSignal
}): Promise<TData> => {
// this.store.batch(() => {
// If the match was in an error state, set it
Expand Down Expand Up @@ -663,7 +672,10 @@ export class LoaderInstance<
this.loader.client?.options.wrapLoaderFn?.(this) ??
this.loader.options.loader

const data = await loaderImpl(this.variables as any, this)
const data = await loaderImpl(this.variables as any, {
loaderInstance: this,
signal: opts?.signal,
})

invariant(
typeof data !== 'undefined',
Expand Down
2 changes: 1 addition & 1 deletion packages/react-loaders/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export function useLoaderInstance<
'useLoaderInstance must be used inside a <LoaderClientProvider> component!',
)

const loader = optsLoader ?? loaderClient.getLoader({ key: opts.key })
const loader = optsLoader ?? loaderClient.getLoader({ key: optsKey })
const loaderInstance = loader.getInstance({
variables: opts?.variables,
})
Expand Down
3 changes: 2 additions & 1 deletion packages/react-router/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,8 @@ export function useSearch<
strict?: TStrict
track?: (search: TSearch) => TSelected
}): TStrict extends true ? TSelected : TSelected | undefined {
const match = useMatch(opts)
const { track, ...matchOpts } = opts as any
const match = useMatch(matchOpts)
useStore(match.__store, (d: any) => opts?.track?.(d.search) ?? d.search, true)

return (match as unknown as RouteMatch).state.search as any
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
// { "path": "packages/react-router-devtools" },
// { "path": "examples/react/kitchen-sink/tsconfig.dev.json" }
]
// "include": ["examples/*"]
// "exclude": ["node_modules"]
}

0 comments on commit 7734419

Please sign in to comment.