
本文详解如何在 Next.js 页面中正确响应 URL 查询参数(如邀请码 token),结合 useRouter 的 isReady 和 useSWR 的条件取数机制,避免 Hooks 调用违规、闪屏及状态竞态问题,实现注册页的优雅条件渲染。
本文详解如何在 next.js 页面中正确响应 url 查询参数(如邀请码 token),结合 `userouter` 的 `isready` 和 `useswr` 的条件取数机制,避免 hooks 调用违规、闪屏及状态竞态问题,实现注册页的优雅条件渲染。
在 Next.js 应用中,常需根据 URL 查询参数(如 ?token=abc123)动态调整页面行为——例如复用注册页支持「带邀请链接的预填充注册」。但若直接在条件分支中调用 useSWR 等 Hook,会违反 React 的 Rules of Hooks,触发 Rendered more hooks than during the previous render 错误。根本原因在于:Hook 调用顺序必须严格一致,而 if 语句会导致某些渲染周期跳过 Hook,破坏一致性。
正确的解法是将条件逻辑前置到 Hook 参数中,而非控制 Hook 的执行与否。useSWR 官方明确支持条件取数(Conditional Fetching):当传入 null 或 false 作为 key 时,SWR 自动跳过请求,且不触发任何副作用。
以下是优化后的完整实现方案:
import { useRouter } from 'next/router'; import useSWR from 'swr'; import axios from 'axios'; import { useAuth } from '@/hooks/useAuth'; // 假设为自定义认证 Hook import PageLoader from '@/components/PageLoader'; const Register = () => { const { query: { token }, isReady, // ✅ 关键:确保路由参数已解析完成 } = useRouter(); const { register, isLoading: isAuthLoading } = useAuth({ middleware: 'guest', redirectIfAuthenticated: '/dashboard', }); // ✅ 正确:key 为 null 时 SWR 不发起请求;isReady 保证 token 不为 undefined const { data: inviteData, error: inviteError, isLoading: isInviteLoading, } = useSWR( isReady && token ? `/api/retrieve-invite/${token}` : null, (url) => axios.get(url).then((res) => res.data.data) ); const submitForm = async (event: React.FormEvent) => { event.preventDefault(); // 表单提交逻辑(含预填充 email 处理) }; // ✅ 统一加载态:等待路由就绪 + 认证初始化 + 邀请数据(若存在) if (!isReady || isAuthLoading || isInviteLoading) { return <PageLoader />; } // ✅ 错误处理:仅当 token 存在且请求失败时展示提示 if (token && inviteError) { return ( <div className="text-red-500 p-4 bg-red-50 rounded"> 邀请码无效或已过期,请检查链接或联系管理员。 </div> ); } // ✅ 渲染主体:此处可安全访问 inviteData?.email 进行预填充 return ( <div className="max-w-md mx-auto p-6"> <h1 className="text-2xl font-bold mb-6"> {token ? '受邀注册' : '创建账户'} </h1> <form onSubmit={submitForm}> <input type="email" name="email" defaultValue={inviteData?.email || ''} placeholder="邮箱地址" disabled={!!inviteData} // 可选:锁定受邀邮箱 className="w-full p-2 border rounded mb-4" /> {/* 其他字段:first_name, last_name 等 */} <button type="submit" disabled={isAuthLoading} className="w-full py-2 bg-blue-600 text-white rounded hover:bg-blue-700" > {token ? '确认受邀注册' : '立即注册'} </button> </form> </div> ); }; export default Register;
关键要点总结:
- ✅ 永远不要在 if / for / 条件表达式中调用 Hook:useSWR 必须在顶层调用,通过 key 参数控制是否取数;
- ✅ 依赖 useRouter().isReady:它标识路由参数(query)已完全解析,避免 token 为 undefined 导致错误请求;
- ✅ 统一加载态判断:将 isReady、认证加载态、邀请数据加载态合并为单一 PageLoader 触发条件,消除“先显示空白表单再闪现预填充”的体验断层;
- ✅ 服务端友好:该方案完全在客户端执行,不影响 SSR/SSG;若需服务端校验 token,应配合 getServerSideProps 实现重定向或初始数据注入;
- ⚠️ 注意错误边界:inviteError 仅在请求失败时存在,需显式判断 token && inviteError,避免对无 token 场景误判。
通过此模式,你不仅能彻底规避 Hooks 报错,还能构建出响应迅速、逻辑清晰、用户体验一致的动态注册流程。