Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Update tRPC client to use new TanStack React Query client instead of Classic React Query Integration #2065

Open
aidansunbury opened this issue Mar 5, 2025 · 6 comments
Labels
🌟 enhancement New feature or request 👨‍👦‍👦free for all Anyone is free to take on this issue and file a PR

Comments

@aidansunbury
Copy link
Contributor

aidansunbury commented Mar 5, 2025

Is your feature request related to a problem? Please describe.

The new API is now the recommended client for integrating with TanStack Query. The introduction blog post states that the classic React Query integration will no longer "receive any significant new features"

Describe the solution you'd like to see

Replace all existing code using the legacy client with code using the updated client, following the migration guide

Describe alternate solutions

Leave CT3A out of date

Additional information

No response

@aidansunbury aidansunbury added the 🌟 enhancement New feature or request label Mar 5, 2025
@acho1833
Copy link

acho1833 commented Mar 8, 2025

+1 for this. I wonder if the project is even supported, last commit is 2 months ago.

@juliusmarminge
Copy link
Member

feel free to file a PR :)

@juliusmarminge juliusmarminge added the 👨‍👦‍👦free for all Anyone is free to take on this issue and file a PR label Mar 21, 2025
@aidansunbury
Copy link
Contributor Author

aidansunbury commented Mar 21, 2025

Working on this currently

@jwoyo
Copy link

jwoyo commented Apr 3, 2025

My solution for gradual adoption:

I understand that this suggestion aims to revamp the whole t3 template to adopt the new pattern that useTRPC built via createTRPCContext from @trpc/tanstack-react-query (!= the older @tanstack/react-query) introduces.

However, I have a couple of t3 projects running and want to gradually adopt the new queryOptions and mutationOptions pattern.

Therefore, I'm using TRPCOptionsProxy to use both approaches in parallel.

// react.ts

// ...

// see here ⬇️
  const [optionsProxy] = useState(() =>
    createTRPCOptionsProxy<AppRouter>({ client: trpcClient, queryClient }),
  );
// ........ ⬆️


  return (
    <QueryClientProvider client={queryClient}>
      <api.Provider
        client={trpcClient}
        queryClient={queryClient}
      >
// see here ⬇️
        <TrpcInteropContext.Provider
          value={{
            optionsProxy,
          }}
        >
          {props.children}
        </TrpcInteropContext.Provider>
// ........ ⬆️
      </api.Provider>
    </QueryClientProvider>
  );
}

// ...

// see here ⬇️
// use this hook like outlined here: https://trpc.io/docs/client/tanstack-react-query/usage
export const useTRPC = () => {
  const { optionsProxy } = useContext(TrpcInteropContext);
  return optionsProxy;
};
// ........ ⬆️

useTRPC is then the new hook that allows patterns with queryOptions and mutationOptions

The diff to the current react.tsx is small. Find the full file here: https://gist.github.com/jwoyo/ae62e8896bccf8e735e249dccc9b492e

I can imagine that we could add this to the documentation. Let me know what you think.

@jwoyo
Copy link

jwoyo commented Apr 3, 2025

This is how react.tsx looks like for a full migration btw.

"use client";

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchStreamLink, loggerLink } from "@trpc/client";
import { createTRPCContext } from '@trpc/tanstack-react-query';
import { type inferRouterInputs, type inferRouterOutputs } from "@trpc/server";
import { useState } from "react";
import SuperJSON from "superjson";
import { createTRPCClient } from '@trpc/client';

import { type AppRouter } from "~/server/api/root";

function makeQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: {
        // With SSR, we usually want to set some default staleTime
        // above 0 to avoid refetching immediately on the client
        staleTime: 60 * 1000,
      },
    },
  });
}
let browserQueryClient: QueryClient | undefined = undefined;
function getQueryClient() {
  if (typeof window === 'undefined') {
    // Server: always make a new query client
    return makeQueryClient();
  } else {
    // Browser: make a new query client if we don't already have one
    // This is very important, so we don't re-make a new client if React
    // suspends during the initial render. This may not be needed if we
    // have a suspense boundary BELOW the creation of the query client
    if (!browserQueryClient) browserQueryClient = makeQueryClient();
    return browserQueryClient;
  }
}

export const { TRPCProvider, useTRPC, useTRPCClient } = createTRPCContext<AppRouter>();

/**
 * Inference helper for inputs.
 *
 * @example type HelloInput = RouterInputs['example']['hello']
 */
export type RouterInputs = inferRouterInputs<AppRouter>;

/**
 * Inference helper for outputs.
 *
 * @example type HelloOutput = RouterOutputs['example']['hello']
 */
export type RouterOutputs = inferRouterOutputs<AppRouter>;

export function TRPCReactProvider(props: { children: React.ReactNode }) {
  const queryClient = getQueryClient();

  const [trpcClient] = useState(() =>
    createTRPCClient<AppRouter>({
      links: [
        loggerLink({
          enabled: (op) =>
            process.env.NODE_ENV === "development" ||
            (op.direction === "down" && op.result instanceof Error),
        }),
        httpBatchStreamLink({
          transformer: SuperJSON,
          url: getBaseUrl() + "/api/trpc",
          headers: () => {
            const headers = new Headers();
            headers.set("x-trpc-source", "nextjs-react");
            return headers;
          },
        }),
      ],
    })
  );

  return (
    <QueryClientProvider client={queryClient}>
      <TRPCProvider trpcClient={trpcClient} queryClient={queryClient}>
        {props.children}
      </TRPCProvider>
    </QueryClientProvider>
  );
}

function getBaseUrl() {
  if (typeof window !== "undefined") return window.location.origin;
  if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;
  return `http://localhost:${process.env.PORT ?? 3000}`;
}

@juliusmarminge
Copy link
Member

Here's a simple example showing how to use both for an incremental migration: https://github.com/juliusmarminge/trpc-interop

Also note that the codemod we've provided should help you migrate a lot of stuff at once

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🌟 enhancement New feature or request 👨‍👦‍👦free for all Anyone is free to take on this issue and file a PR
Projects
None yet
Development

No branches or pull requests

4 participants