I am currently exploring TanStack Start (with React).
At What are Server Functions? page we have example of server function that can be used with useQuery (from TanStack Query, to fetch data from endpoint).
Example is as follows:
const getServerPosts = createServerFn().handler(async () => {...});
// In a component
function PostList() {
const getPosts = useServerFn(getServerPosts)
const { data } = useQuery({
queryKey: ['posts'],
queryFn: () => getPosts(),
})
}
However, if I remove useServerFn and use getServerPosts directly:
const getServerPosts = createServerFn().handler(async () => {...});
// In a component
function PostList() {
const { data } = useQuery({
queryKey: ['posts'],
queryFn: () => getServerPosts(),
})
}
Then everything keeps working just as fine. When I inspect network requests in dev tools, there's nothing suspicious going on, I mean the requests are the same, payloads returned are also the same, the application itself keeps working fine.
So the question is what is the purpose of useServerFn hooks and what it help us with?
After looking up in the code after building, i have found what is produced regarding useServerFn:
function useServerFn(serverFn) {
const router = useRouter();
return React.useCallback(
async (...args) => {
try {
const res = await serverFn(...args);
if (isRedirect(res)) {
throw res;
}
return res;
} catch (err) {
if (isRedirect(err)) {
err.options._fromLocation = router.state.location;
return router.navigate(router.resolveRedirect(err).options);
}
throw err;
}
},
[router, serverFn]
);
}
We can see it wraps logic in useCallback and then performs action, wrapping it in try..catch to handle redirects gracefully and work with throw redirect(). But seems not to do anything else.
Now below explanation make a lot more sense.
ORIGINAL ANSWER
After discussing it on TanStack discord channel, I got one idea:
It is to allow client side handling of execution of the function.
As long as function returns data, it is OK, but the things change when there's throw in the server function.
Then this throw is not handled correctly on client, or not handled at all.
This is important when using things like redirect. Let's consider such server function:
export const throwRedirect = createServerFn().handler(() => {
throw redirect({ to: '/', statusCode: 302 })
})
const redirectFn = useServerFn(throwRedirect)
Then below usage, that won't do anything, as there will be no client handling to catch and perform redirect:
<button onClick={() => throwRedirect ()}>
Trigger Redirect To Main Page (from server)
</button>
This is valid redirection, where client is able to catch and handle thrown redirect:
<button onClick={() => redirectFn()}>
Trigger Redirect To Main Page (from server)
</button>