reactjs - Next.js hvordan bruke tRPC med serverkomponenter på riktig måte
Jeg holder på å lære T3 stack med App-router. Men jeg støtte på et problem med å bruke tRPC. 90 % av tutorialene på internett viser hvordan man kombinerer tRPC med Next.js ved å bruke hooks, for eksempel:
const {data: posts, isLoading} = api.posts.all.useQuery({});Men det er en hook! Den kan bare brukes i en klientkomponent. Så det ødelegger SSR, SSG, hva som helst. Jeg vet ikke hvorfor alle på YouTube viser denne måten å hente data på. Jeg har sett en video som dette på den offisielle Theo T3 YT-kanalen.
Så, hvordan får man riktig data i serverkomponenter ved å bruke tRPC med next.js?
Jeg fant 2 måter, men jeg har ikke helt funnet dem ut.
- Måte 1 - ved å bruke
createTRPCProxyClientsom T3 CLI tilbyr. Jeg lager tRPCserverApi, deretter i en serverkomponent laster jeg data og sender det som props til en CLIENT-komponent som bruker tRPC-hooken useQuery med initialData. Det fungerer, greit nok, men det er så komplisert, ikke sant? I tillegg krever detnoStore()-kall før hverserverAPI- det er så forvirrende! UtennoStore()fungerer dev-serveren helt fint, men når jeg kjørernpm run buildfår jeg en feil. P.S. beklager for mye kode
// trpc/server.ts
import "server-only";
import {
createTRPCProxyClient,
loggerLink,
TRPCClientError,
} from "@trpc/client";
import { callProcedure } from "@trpc/server";
import { observable } from "@trpc/server/observable";
import { type TRPCErrorResponse } from "@trpc/server/rpc";
import { headers } from "next/headers";
import { cache } from "react";
import { appRouter, type AppRouter } from "@/server/api/root";
import { createTRPCContext } from "@/server/api/trpc";
import { transformer } from "./shared";
/**
* This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when
* handling a tRPC call from a React Server Component.
*/
const createContext = cache(() => {
const heads = new Headers(headers());
heads.set("x-trpc-source", "rsc");
return createTRPCContext({
headers: heads,
});
});
export const serverApi = createTRPCProxyClient<AppRouter>({
transformer,
links: [
/**
* Custom RSC link that lets us invoke procedures without using http requests. Since Server
* Components always run on the server, we can just call the procedure as a function.
*/
() =>
({ op }) =>
observable((observer) => {
createContext()
.then((ctx) => {
return callProcedure({
procedures: appRouter._def.procedures,
path: op.path,
rawInput: op.input,
ctx,
type: op.type,
});
})
.then((data) => {
observer.next({ result: { data } });
observer.complete();
})
.catch((cause: TRPCErrorResponse) => {
observer.error(TRPCClientError.from(cause));
});
}),
],
});
// app/page.tsx
// ...
import { unstable_noStore as noStore } from "next/cache";
import { serverApi } from "@/trpc/server";
const ResentTweetsPage: NextPage = async ({}) => {
noStore();
// ^^^^^^^^ How to remove it?
const tweets = await serverApi.tweet.infiniteFeed.query(
{},
);
return (
<TabsWrapper
current="Recent"
>
<RecentTweets
initialTweets={tweets}
/>
</TabsWrapper>
);
};
export default ResentTweetsPage
// components/RecentTweets
'use client';
import { api } from "@/trpc/react";
import { serverApi } from "@/trpc/server";
export const RecentTweets: FC<{
initialTweets: Awaited<ReturnType<(typeof serverApi.tweet.infiniteFeed.query)>>
}> = ({
initialTweets
}) => {
const tweets = api.tweet.infiniteFeed.useInfiniteQuery(
{},
{
getNextPageParam: lastPage => lastPage.nextCursor,
// Thanks for it the page render this data at first loading
initialData: {
pageParams: [],
pages: [
initialTweets
]
},
refetchOnMount: false,
refetchOnReconnect: false,
refetchInterval: 1000 * 60
}
);
return (
<InfiniteTweetList
tweets={
tweets.data?.pages.flatMap(page => page.tweets)
}
isError={tweets.isError}
isLoading={tweets.isLoading}
hasMore={Boolean(tweets.hasNextPage)}
fetchNewTweets={tweets.fetchNextPage}
/>
);
};- Måte 2 - ved å bruke
createCaller. Jeg må fortsatt sendeinitialTweetstil klientkomponenten, og det finnes ingen.query()iawait serverClient.tweet.infiniteFeed({}). Jeg ville foretrekke denne metoden, men når jeg kjørernpm run build, får jeg denne feilen:Siden kunne ikke gjengis statisk fordi den brukte 'headers'og jeg har ikke funnet ut hvordan jeg fikser det.
// trpc/serverClient.ts
import { appRouter } from "@/server/api/root";
import { httpBatchLink } from "@trpc/client";
import { createTRPCContext } from "@/server/api/trpc";
export const serverClient = appRouter.createCaller(
await createTRPCContext({} as any)
)
// app/page.tsx
import {NextPage} from 'next';
import React from "react";
import { RecentTweets } from "@/components/RecentTweets";
import { serverClient } from "@/trpc/serverClient";
import { TabsWrapper } from "@/app/feed/components/TabsWrapper";
const ResentTweetsPage: NextPage = async ({}) => {
const tweets = await serverClient.tweet.infiniteFeed(
{}
);
return (
<TabsWrapper
current="Recent"
>
<RecentTweets
initialTweets={tweets}
/>
</TabsWrapper>
);
};
export default ResentTweetsPageTakk for hjelpen