T3-app使用useSuspenseQuery构建超时,如何正确使用?
问题背景
我开发T3 App时遇到构建超时错误,将代码中:
const [leads] = api.lead.getLeads.useSuspenseQuery();
替换为:
const { data: leads } = api.lead.getLeads.useQuery();
后构建成功。想明确两个问题:
- 如何正确使用useSuspenseQuery?我操作错在哪里?
- 除了无需处理加载/错误状态,useSuspenseQuery还有哪些优势?
完整错误信息
Failed to build /page: / (attempt 1 of 3) because it took more than 60 seconds. Retrying again shortly.
Failed to build /page: / (attempt 2 of 3) because it took more than 60 seconds. Retrying again shortly.
Failed to build /page: / after 3 attempts.
Export encountered an error on /page: /, exiting the build.
⨯ Static worker exited with code: 1 and signal: null
相关代码
首页服务端组件
import { api, HydrateClient } from "@/trpc/server"; import { Leads } from "./_components/Leads/Leads"; export default async function Home() { await api.lead.getLeads.prefetch(); return ( <HydrateClient> <Leads /> </HydrateClient> ); }
使用useSuspenseQuery的客户端组件LeadsTable
"use client"; import { api } from "@/trpc/react"; import { Table } from "../../UI"; import { columns } from "./LeadsTable.columns"; export const LeadsTable = () => { const [leads] = api.lead.getLeads.useSuspenseQuery(); return ( <Table rows={leads} columns={columns} initialState={{ sorting: { sortModel: [ { field: "id", sort: "desc", }, ], }, }} /> ); };
问题分析与解决
1. 错误原因:缺少Suspense边界
你直接在客户端组件中使用useSuspenseQuery,但没有给该组件套上React的Suspense边界。在Next.js静态构建(SSG)过程中,客户端组件触发Suspense后没有对应的 fallback 处理,导致构建进程陷入无限等待,最终超时失败。
2. 正确使用useSuspenseQuery的方式
要让useSuspenseQuery正常工作,必须给使用它的组件添加Suspense边界:
方式一:在父客户端组件中包裹
如果Leads是客户端组件,修改代码如下:
"use client"; import { Suspense } from "react"; import { LeadsTable } from "./LeadsTable"; export const Leads = () => { return ( <Suspense fallback={<div>加载中...</div>}> <LeadsTable /> </Suspense> ); };
方式二:在服务端首页组件中包裹
直接在服务端的Home组件里给Leads加上Suspense边界:
import { Suspense } from "react"; import { api, HydrateClient } from "@/trpc/server"; import { Leads } from "./_components/Leads/Leads"; export default async function Home() { await api.lead.getLeads.prefetch(); return ( <HydrateClient> <Suspense fallback={<div>加载中...</div>}> <Leads /> </Suspense> </HydrateClient> ); }
这样处理后,静态构建时就能正确识别Suspense逻辑,不会出现超时,同时客户端也能正常读取服务端预取的数据。
3. useSuspenseQuery的额外优势
除了不用手动处理isLoading、error状态外,它还有这些实用特性:
- 代码更简洁:直接获取数据,无需写大量条件渲染分支,逻辑更清晰。
- 兼容React Suspense生态:可以配合代码分割、懒加载等特性,实现更细粒度的加载状态控制。
- 数据优先的渲染逻辑:组件仅在数据就绪后渲染,避免了未就绪数据导致的UI异常。
- 自动状态清理:组件卸载时会自动清理Suspense相关状态,降低内存泄漏风险。
内容的提问来源于stack exchange,提问作者Itay Tur

