Next.js注册表单处理邀请Token遇加载问题及Hook报错求助
问题分析与解决方案
核心问题原因
- 钩子调用违规:
useSWR被放在条件判断块中,违反React钩子必须在组件顶层调用的规则,导致Rendered more hooks than during the previous render错误。 - 加载状态混乱:多个加载状态(
isLoading、loading、SWR请求状态)未统一处理,请求Token有效性期间没有正确显示加载器。 - 渲染逻辑顺序错误:Token验证请求完成前,组件直接渲染了标准表单,导致页面先显示标准表单再更新。
修正后的完整代码
import { useState, useEffect } from 'react' import { useRouter } from 'next/router' import useSWR from 'swr' import axios from 'axios' import PageLoader from './PageLoader' // 根据实际路径调整 const Register = () => { const router = useRouter() const { register, isLoading: authLoading } = useAuth({ middleware: 'guest', redirectIfAuthenticated: '/dashboard', }) const [inviteToken, setInviteToken] = useState('') const [formLoading, setFormLoading] = useState(false) const [errors, setErrors] = useState([]) const [inviteEmail, setInviteEmail] = useState('') // 需补充表单自身的状态定义 const [firstName, setFirstName] = useState('') const [lastName, setLastName] = useState('') const [email, setEmail] = useState('') // 监听URL中的token变化,更新inviteToken useEffect(() => { if (router.query.token?.length > 0) { setInviteToken(router.query.token) setErrors([]) setInviteEmail('') } else { setInviteToken('') setInviteEmail('') } }, [router.query.token]) // 顶层调用useSWR,仅当存在inviteToken时发起请求 const { data: inviteData, error: inviteError, isLoading: isInviteLoading } = useSWR( inviteToken ? `/api/retrieve-invite/${inviteToken}` : null, (url) => axios.get(url).then(res => res.data.data) ) // 处理Token验证结果 useEffect(() => { if (inviteError) { setErrors(['Token无效']) setInviteEmail('') } else if (inviteData) { setInviteEmail(inviteData.email) // 假设接口返回数据包含email字段 setErrors([]) } }, [inviteData, inviteError]) // 表单提交逻辑 const submitForm = async (event) => { event.preventDefault() setFormLoading(true) try { await register({ first_name: firstName, last_name: lastName, email: inviteEmail || email }) } catch (err) { setErrors(err.response?.data?.errors || ['注册失败']) } finally { setFormLoading(false) } } // 统一处理所有加载状态 if (authLoading || isInviteLoading || formLoading) { return <PageLoader /> } return ( <div className="register-container"> {/* 错误提示 */} {errors.length > 0 && ( <div className="error-message"> {errors.map((err, idx) => <p key={idx}>{err}</p>)} </div> )} {/* 注册表单 */} <form onSubmit={submitForm}> {/* 邮箱输入:邀请注册时预填并禁用 */} <div> <label>邮箱</label> <input type="email" value={inviteEmail || email} onChange={(e) => !inviteEmail && setEmail(e.target.value)} disabled={!!inviteEmail} required /> </div> <div> <label>名字</label> <input type="text" value={firstName} onChange={(e) => setFirstName(e.target.value)} required /> </div> <div> <label>姓氏</label> <input type="text" value={lastName} onChange={(e) => setLastName(e.target.value)} required /> </div> <button type="submit" disabled={formLoading}> {formLoading ? '注册中...' : '注册'} </button> </form> </div> ) } export default Register
关键修正点说明
- 钩子规则修复:将
useSWR移至组件顶层,通过传递null作为请求URL,实现仅当存在Token时发起请求的逻辑,避免条件性调用钩子。 - 统一加载状态:整合
authLoading(登录状态验证)、isInviteLoading(Token验证请求)、formLoading(表单提交)三个状态,任一状态为true时显示加载器。 - Token逻辑优化:通过
useEffect监听URL中token的变化,自动更新验证状态;验证完成后根据结果设置预填邮箱或错误提示,确保请求过程中不会提前渲染表单。 - 表单逻辑调整:邀请注册时自动预填邮箱并禁用输入,提交时优先使用预填邮箱,保证邀请场景的正确性。
内容的提问来源于stack exchange,提问作者BigJobbies
相关产品推荐
相关产品推荐

