Vercel部署项目报错:/auth-callback页面需将useSearchParams()包裹在Suspense边界中
解决Next.js部署到Vercel时的useSearchParams Suspense边界错误
部署Next.js项目到Vercel时,/auth-callback页面触发错误:useSearchParams()应包裹在Suspense边界内。这是因为在App Router模式下,useSearchParams是客户端专属钩子,若在服务端组件中使用会触发CSR回退,必须通过Suspense或客户端组件标记来处理。
解决办法
办法一:将页面标记为客户端组件(推荐)
直接在页面顶部添加'use client';指令,让整个页面在客户端渲染,无需额外Suspense包裹,适配页面本身的客户端操作逻辑。
修改后的完整代码:
'use client'; import React, { useEffect } from 'react'; import { useRouter, useSearchParams } from "next/navigation"; import { trpc } from "../_trpc/client"; import { Loader2 } from "lucide-react"; const Page = () => { const router = useRouter(); const searchParams = useSearchParams(); const origin = searchParams.get("origin"); const { data, error, isLoading } = trpc.authCallback.useQuery(undefined, { retry: true, retryDelay: 500, }); useEffect(() => { if (error && error.data?.code === "UNAUTHORIZED") { router.push("/sign-in"); } else if (!isLoading && !error) { router.push(origin ? `/${origin}` : "/dashboard"); } }, [isLoading, error, origin, router]); return ( <div className="w-full mt-24 flex justify-center"> <div className="flex flex-col items-center gap-2"> <Loader2 className="h-8 w-8 animate-spin text-zinc-800" /> <h3 className="font-semibold text-xl"> Setting up your account... </h3> <p>You will be redirected automatically.</p> </div> </div> ); }; export default Page;
办法二:用Suspense包裹客户端逻辑组件
若不想将整个页面设为客户端组件,可拆分逻辑到子组件,再用Suspense包裹该子组件。
- 创建客户端子组件
AuthCallbackClient.js:
'use client'; import React, { useEffect } from 'react'; import { useRouter, useSearchParams } from "next/navigation"; import { trpc } from "../_trpc/client"; import { Loader2 } from "lucide-react"; export const AuthCallbackClient = () => { const router = useRouter(); const searchParams = useSearchParams(); const origin = searchParams.get("origin"); const { data, error, isLoading } = trpc.authCallback.useQuery(undefined, { retry: true, retryDelay: 500, }); useEffect(() => { if (error && error.data?.code === "UNAUTHORIZED") { router.push("/sign-in"); } else if (!isLoading && !error) { router.push(origin ? `/${origin}` : "/dashboard"); } }, [isLoading, error, origin, router]); return ( <div className="flex flex-col items-center gap-2"> <Loader2 className="h-8 w-8 animate-spin text-zinc-800" /> <h3 className="font-semibold text-xl"> Setting up your account... </h3> <p>You will be redirected automatically.</p> </div> ); };
- 修改原页面,用Suspense包裹子组件:
import React, { Suspense } from 'react'; import { AuthCallbackClient } from './AuthCallbackClient'; const Page = () => { return ( <div className="w-full mt-24 flex justify-center"> <Suspense fallback={ <div className="flex flex-col items-center gap-2"> <Loader2 className="h-8 w-8 animate-spin text-zinc-800" /> <h3 className="font-semibold text-xl">Loading...</h3> </div> }> <AuthCallbackClient /> </Suspense> </div> ); }; export default Page;
说明
优先选第一种办法,因为页面核心逻辑(读取URL参数、路由跳转、调用trpc客户端钩子)都是客户端操作,直接标记为客户端组件更简洁,无需额外拆分组件。
内容的提问来源于stack exchange,提问作者Rowny Robert
相关产品推荐
相关产品推荐

