examples/03-query-owned-rsc.tsx
examples/03-query-owned-rsc.tsxBrowse 13 files
660 tokens
2,532 bytes
Token encoding: o200k_base
Snapshot 8e164d2
← Back to SKILL.md
1// Query-owned RSC pattern.2// Assumes your router context provides a queryClient for SSR prefetch.3 4import type { ReactNode } from 'react'5import { createFileRoute } from '@tanstack/react-router'6import { createServerFn } from '@tanstack/react-start'7import {8 CompositeComponent,9 createCompositeComponent,10} from '@tanstack/react-start/rsc'11import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query'12import { z } from 'zod'13 14// Replace with your own data layer15declare const db: {16 posts: {17 findById(postId: string): Promise<{18 id: string19 title: string20 body: string21 }>22 update(23 postId: string,24 patch: { title?: string; body?: string },25 ): Promise<void>26 }27}28 29const getPostRsc = createServerFn({ method: 'GET' })30 .validator(z.object({ postId: z.string() }))31 .handler(async ({ data }) => {32 const post = await db.posts.findById(data.postId)33 34 const src = await createCompositeComponent<{35 renderActions?: (args: { postId: string }) => ReactNode36 }>((props) => (37 <article>38 <h1>{post.title}</h1>39 <p>{post.body}</p>40 <footer>{props.renderActions?.({ postId: post.id })}</footer>41 </article>42 ))43 44 return { src }45 })46 47const updatePost = createServerFn({ method: 'POST' })48 .validator(49 z.object({50 postId: z.string(),51 title: z.string().optional(),52 body: z.string().optional(),53 }),54 )55 .handler(async ({ data }) => {56 await db.posts.update(data.postId, {57 title: data.title,58 body: data.body,59 })60 })61 62const postQueryOptions = (postId: string) => ({63 queryKey: ['post-rsc', postId],64 structuralSharing: false,65 queryFn: () => getPostRsc({ data: { postId } }),66 staleTime: 5 * 60 * 1000,67})68 69export const Route = createFileRoute('/posts/$postId')({70 loader: async ({ context, params }) => {71 await context.queryClient.ensureQueryData(postQueryOptions(params.postId))72 },73 component: PostPage,74})75 76function PostPage() {77 const { postId } = Route.useParams()78 const queryClient = useQueryClient()79 80 const { data } = useSuspenseQuery(postQueryOptions(postId))81 82 const handleRename = async () => {83 await updatePost({ data: { postId, title: 'Updated title' } })84 await queryClient.invalidateQueries({ queryKey: ['post-rsc', postId] })85 }86 87 return (88 <CompositeComponent89 src={data.src}90 renderActions={({ postId }) => (91 <button type="button" onClick={handleRename}>92 Refresh {postId}93 </button>94 )}95 />96 )97}98