diff --git a/docs/router/integrations/query.md b/docs/router/integrations/query.md index 707f90900c1..bc5f647b9ad 100644 --- a/docs/router/integrations/query.md +++ b/docs/router/integrations/query.md @@ -216,7 +216,7 @@ export const Route = createFileRoute('/user/$id')({ ## Redirect handling -If a query or mutation throws a `redirect(...)`, the integration intercepts it on the client and performs a router navigation. +If a query or mutation throws a `redirect(...)`, the integration intercepts it on the client and performs a router navigation. You do not need to individually wrap [server functions](/start/latest/docs/framework/react/guide/server-functions) in `useServerFn` if they are called in a `queryFn` or `mutationFn`. - Enabled by default - Disable with `handleRedirects: false` if you need custom handling diff --git a/docs/start/framework/react/guide/server-functions.md b/docs/start/framework/react/guide/server-functions.md index 68a6b8ac72d..ed4fa22ee85 100644 --- a/docs/start/framework/react/guide/server-functions.md +++ b/docs/start/framework/react/guide/server-functions.md @@ -93,6 +93,9 @@ function PostList() { } ``` +> [!NOTE] +> If you use TanStack Query with the `@tanstack/react-router-ssr-query` integration package, wrapping server functions called in `queryFn` or `mutationFn` with `useServerFn` is not required. See the [Query integration guide](/router/latest/docs/integrations/query#redirect-handling) for more details. + ## File Organization For larger applications, consider organizing server-side code into separate files. Here's one approach: @@ -257,7 +260,7 @@ export const looseOutputServerFn = createServerFn({ ## Error Handling & Redirects -Server functions can throw errors, redirects, and not-found responses that are handled automatically when called from route lifecycles or components using `useServerFn()`. +Server functions can throw errors, redirects, and not-found responses that are handled automatically when called from route lifecycles, components using `useServerFn()`, or inside TanStack Query `queryFn` and `mutationFn` functions when the [`@tanstack/react-router-ssr-query` integration package](/router/latest/docs/integrations/query) is used. ### Basic Errors diff --git a/docs/start/framework/react/guide/tanstack-query.md b/docs/start/framework/react/guide/tanstack-query.md index 7797ae306be..3753b21b461 100644 --- a/docs/start/framework/react/guide/tanstack-query.md +++ b/docs/start/framework/react/guide/tanstack-query.md @@ -85,6 +85,8 @@ Both calls use the same query key and options. The loader populates the request' For a parameterized resource, include its identity in the key, such as `['post', postId]`. If search values affect the result, validate them and include them in both `loaderDeps` and the query key. Keep secrets out of keys and serialized query data. +Note that if you are using server functions and you have the [integration](#install-and-create-a-queryclient-per-router) installed, you do not need to use `useServerFn` to properly handle thrown errors or redirects. Simply call the server function in a `queryFn` or `mutationFn` normally. + ## Await critical data and stream secondary data Await queries that determine the page's main content, title, authorization, redirects, or whether it exists. Return title and description data from the loader when a route's `head` needs them, as the example's post route does. @@ -95,13 +97,12 @@ The example's existing `/deferred` route shows a separate Suspense boundary. See ## Invalidate Query after a server mutation -The preference example validates the display name and writes its cookie inside a POST server function. Its `src/start.ts` enables Start's CSRF middleware for server-function requests. The component calls that function through `useServerFn`, then invalidates the query that reads the cookie: +The preference example validates the display name and writes its cookie inside a POST server function. Its `src/start.ts` enables Start's CSRF middleware for server-function requests. The component calls that function, then invalidates the query that reads the cookie: ```tsx const queryClient = useQueryClient() -const save = useServerFn(saveReaderName) const mutation = useMutation({ - mutationFn: (name: string) => save({ data: name }), + mutationFn: (name: string) => saveReaderName({ data: name }), onSuccess: () => queryClient.invalidateQueries({ queryKey: readerNameOptions.queryKey }), }) diff --git a/examples/react/start-basic-react-query/src/routes/preferences.tsx b/examples/react/start-basic-react-query/src/routes/preferences.tsx index 603257fc0fd..a6ede867fe6 100644 --- a/examples/react/start-basic-react-query/src/routes/preferences.tsx +++ b/examples/react/start-basic-react-query/src/routes/preferences.tsx @@ -4,7 +4,6 @@ import { useSuspenseQuery, } from '@tanstack/react-query' import { createFileRoute, useHydrated } from '@tanstack/react-router' -import { useServerFn } from '@tanstack/react-start' import { readerNameOptions, saveReaderName } from '../utils/preferences' export const Route = createFileRoute('/preferences')({ @@ -19,10 +18,9 @@ export const Route = createFileRoute('/preferences')({ function Preferences() { const { data: name } = useSuspenseQuery(readerNameOptions) const queryClient = useQueryClient() - const save = useServerFn(saveReaderName) const hydrated = useHydrated() const mutation = useMutation({ - mutationFn: (name: string) => save({ data: name }), + mutationFn: (name: string) => saveReaderName({ data: name }), onSuccess: () => queryClient.invalidateQueries({ queryKey: readerNameOptions.queryKey }), })