javascripttypescriptreact-nativeexpotrpc.io

Expo + tRPC: Unable to retrieve application context. Did you forget to wrap your App inside `withTRPC` HoC?


I have pretty simple tRPC server:

// server.ts
import { initTRPC } from "@trpc/server";
import { z } from "zod";

const t = initTRPC.create();

export const appRouter = t.router({
    greeting: t.procedure
        .input(
            z.object({
                name: z.string().optional()
            }))
        .query(({ input }) => {
            return {
                message: `hello ${input.name ?? "world"}`
            };
        }),
});

export type AppRouter = typeof appRouter;

And here's the client (almost vanilla expo-template-blank-typescript):

// App.tsx
import { StyleSheet, Text, View } from 'react-native';
import { StatusBar } from 'expo-status-bar';

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createTRPCReact, httpBatchLink } from '@trpc/react-query';
import type { AppRouter } from '../backend';

const queryClient = new QueryClient();

const trpc = createTRPCReact<AppRouter>();

const trpcClient = trpc.createClient({
  links: [
    httpBatchLink({
      url: "http://localhost:9696"
    })
  ]
})

export default function App() {
  return (
    <trpc.Provider client={trpcClient} queryClient={queryClient}>
      <QueryClientProvider client={queryClient}>
        <View style={styles.container}>
          <Text>Open up App.tsx to start working on your app!</Text>
          <Text>{trpc.greeting.useQuery({ name: "expo client" }).data?.message}</Text>
          <StatusBar style="auto" />
        </View>
      </QueryClientProvider>
    </trpc.Provider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

And I'm getting a faily generic "Did you forget to wrap your App inside withTRPC HoC?" error:

expo on web error

I'm struggling to understand what went wrong here as there's trpc.Provider and QueryClientProvider already in the App's render tree and properly setup.

I'd really appreciate if I could get a hint at what's wrong with this setup!


Solution

  • I'm not sure why but I think I found a solution.

    Basically, I created another super simple Foo.tsx component and made tRPC calls from there, it worked perfectly.

    Here's Foo.tsx:

    import { View, Text } from "react-native";
    
    import { trpc } from "./App";
    
    export default function Foo() {
        return (
            <View>
                <Text>{trpc.greeting.useQuery({ name: "expo client" }).data?.message}</Text>
            </View>
        );
    }
    

    And here's the new App.tsx with diff markups:

    import { StyleSheet, Text, View } from 'react-native';
    import { StatusBar } from 'expo-status-bar';
    
    import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
    import { createTRPCReact, httpBatchLink } from '@trpc/react-query';
    import type { AppRouter } from '../backend';
    
    +++ import Foo from './Foo';
    
    const queryClient = new QueryClient();
    
    --- const trpc = createTRPCReact<AppRouter>();
    +++ export const trpc = createTRPCReact<AppRouter>();
    
    const trpcClient = trpc.createClient({
      links: [
        httpBatchLink({
          url: "http://localhost:9696"
        })
      ]
    })
    
    export default function App() {
      return (
        <trpc.Provider client={trpcClient} queryClient={queryClient}>
          <QueryClientProvider client={queryClient}>
            <View style={styles.container}>
              <Text>Open up App.tsx to start working on your app!</Text>
    ---       <Text>{trpc.greeting.useQuery({ name: "expo client" }).data?.message}</Text>
    +++       <Foo />
              <StatusBar style="auto" />
            </View>
          </QueryClientProvider>
        </trpc.Provider>
      );
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        backgroundColor: '#fff',
        alignItems: 'center',
        justifyContent: 'center',
      },
    });