跳转到内容

React 连接到 tRPC

Nx 的 AWS 插件提供了一个生成器,可快速将您的 tRPC API 与 React 网站集成。它会配置所有必要的后端连接设置,包括 AWS IAM 身份验证支持和正确的错误处理。该集成可在前端与 tRPC 后端之间提供完整的端到端类型安全。

前提条件

使用此生成器前,请确保您的 React 应用具备:

  1. 渲染应用的 main.tsx 文件
  2. 将自动注入 tRPC 提供程序的 <App/> JSX 元素
  3. 可正常运行的 tRPC 后端(需使用 tRPC 后端生成器创建)
所需 main.tsx 结构示例
import { StrictMode } from 'react';
import * as ReactDOM from 'react-dom/client';
import App from './app/app';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTTMLHtmlElement,
);
root.render(
<StrictMode>
<App />
</StrictMode>,
);

使用说明

运行生成器

  1. 安装 Nx Console VSCode Plugin 如果您尚未安装
  2. 在VSCode中打开Nx控制台
  3. 点击 Generate (UI) 在"Common Nx Commands"部分
  4. 搜索 @aws/nx-plugin - api-connection
  5. 填写必需参数
    • 点击 Generate

    选项配置

    参数 类型 默认值 描述
    sourceProject 必需 string - The source project which will call the API
    targetProject 必需 string - The target project containing your API
    auth string IAM Authentication strategy (choose from IAM or None)

    生成器输出

    生成器将在您的 React 应用中创建以下结构:

    • 文件夹src
      • 文件夹components
        • 文件夹TrpcClients
          • index.tsx
          • TrpcProvider.tsx 可复用多个 tRPC API 的 Provider
          • TrpcApis.tsx 包含所有 tRPC API 连接的对象
          • TrpcClientProviders.tsx 配置 tRPC 客户端与后端 schema 的绑定
        • QueryClientProvider.tsx TanStack React Query 客户端 Provider
      • 文件夹hooks
        • useSigV4.tsx 使用 SigV4 签名 HTTP 请求的钩子(仅限 IAM)
        • use<ApiName>.tsx 对应后端 API 的钩子。ApiName 将解析为 API 名称

    同时安装以下依赖项:

    • @trpc/client
    • @trpc/tanstack-react-query
    • @tanstack/react-query
    • aws4fetch(使用 IAM 认证时需要)

    使用生成代码

    使用 tRPC 钩子

    生成器提供 use<ApiName> 钩子用于访问类型安全的 tRPC 客户端:

    import { useQuery, useMutation } from '@tanstack/react-query';
    import { useMyApi } from './hooks/useMyApi';
    function MyComponent() {
    const trpc = useMyApi();
    // 查询示例
    const { data, isLoading, error } = useQuery(trpc.users.list.queryOptions());
    // 变更示例
    const mutation = useMutation(trpc.users.create.mutationOptions());
    const handleCreate = () => {
    mutation.mutate({
    name: 'John Doe',
    email: 'john@example.com',
    });
    };
    if (isLoading) return <div>加载中...</div>;
    return (
    <ul>
    {data.map((user) => (
    <li key={user.id}>{user.name}</li>
    ))}
    </ul>
    );
    }

    错误处理

    集成包含内置错误处理机制,可正确处理 tRPC 错误:

    function MyComponent() {
    const trpc = useMyApi();
    const { data, error } = useQuery(trpc.users.list.queryOptions());
    if (error) {
    return (
    <div>
    <h2>发生错误:</h2>
    <p>{error.message}</p>
    {error.data?.code && <p>错误代码:{error.data.code}</p>}
    </div>
    );
    }
    return (
    <ul>
    {data.map((user) => (
    <li key={user.id}>{user.name}</li>
    ))}
    </ul>
    );
    }

    最佳实践

    处理加载状态

    始终处理加载和错误状态以提升用户体验:

    function UserList() {
    const trpc = useMyApi();
    const users = useQuery(trpc.users.list.queryOptions());
    if (users.isLoading) {
    return <LoadingSpinner />;
    }
    if (users.error) {
    return <ErrorMessage error={users.error} />;
    }
    return (
    <ul>
    {users.data.map((user) => (
    <li key={user.id}>{user.name}</li>
    ))}
    </ul>
    );
    }

    乐观更新

    使用乐观更新提升用户体验:

    import { useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
    function UserList() {
    const trpc = useMyApi();
    const users = useQuery(trpc.users.list.queryOptions());
    const queryClient = useQueryClient();
    const deleteMutation = useMutation(
    trpc.users.delete.mutationOptions({
    onMutate: async (userId) => {
    // 取消进行中的请求
    await queryClient.cancelQueries(trpc.users.list.queryFilter());
    // 获取当前数据快照
    const previousUsers = queryClient.getQueryData(
    trpc.users.list.queryKey(),
    );
    // 乐观删除用户
    queryClient.setQueryData(trpc.users.list.queryKey(), (old) =>
    old?.filter((user) => user.id !== userId),
    );
    return { previousUsers };
    },
    onError: (err, userId, context) => {
    // 出错时恢复原始数据
    queryClient.setQueryData(
    trpc.users.list.queryKey(),
    context?.previousUsers,
    );
    },
    }),
    );
    return (
    <ul>
    {users.map((user) => (
    <li key={user.id}>
    {user.name}
    <button onClick={() => deleteMutation.mutate(user.id)}>删除</button>
    </li>
    ))}
    </ul>
    );
    }

    数据预取

    预取数据以提升性能:

    function UserList() {
    const trpc = useMyApi();
    const users = useQuery(trpc.users.list.queryOptions());
    const queryClient = useQueryClient();
    // 悬停时预取用户详情
    const prefetchUser = async (userId: string) => {
    await queryClient.prefetchQuery(trpc.users.getById.queryOptions(userId));
    };
    return (
    <ul>
    {users.map((user) => (
    <li key={user.id} onMouseEnter={() => prefetchUser(user.id)}>
    <Link to={`/users/${user.id}`}>{user.name}</Link>
    </li>
    ))}
    </ul>
    );
    }

    无限查询

    使用无限查询处理分页:

    function UserList() {
    const trpc = useMyApi();
    const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
    useInfiniteQuery(
    trpc.users.list.infiniteQueryOptions(
    { limit: 10 },
    {
    getNextPageParam: (lastPage) => lastPage.nextCursor,
    },
    ),
    );
    return (
    <div>
    {data?.pages.map((page) =>
    page.users.map((user) => <UserCard key={user.id} user={user} />),
    )}
    {hasNextPage && (
    <button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}>
    {isFetchingNextPage ? '加载中...' : '加载更多'}
    </button>
    )}
    </div>
    );
    }

    需注意:无限查询只能用于输入参数包含 cursor 的存储过程。

    类型安全

    该集成提供完整的端到端类型安全。您的 IDE 将为所有 API 调用提供完整的自动补全和类型检查:

    function UserForm() {
    const trpc = useMyApi();
    // ✅ 输入类型完全校验
    const createUser = trpc.users.create.useMutation();
    const handleSubmit = (data: CreateUserInput) => {
    // ✅ 输入不匹配 schema 时会报类型错误
    createUser.mutate(data);
    };
    return <form onSubmit={handleSubmit}>{/* ... */}</form>;
    }

    类型会自动从后端路由和 schema 定义中推断,确保 API 的任何变更都能立即反映在前端代码中,无需重新构建。

    更多信息

    更多信息请参考 tRPC TanStack React Query 文档

    也可直接查阅 TanStack Query 文档