
本文详解如何在 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 报错,还能构建出响应迅速、逻辑清晰、用户体验一致的动态注册流程。









