Next.js + shadcn UI登录表单报错:渲染时更新组件问题排查
问题排查与修复方案
问题分析
- 跨组件更新警告根源:你在
FormField组件上直接添加了disabled={isPending}属性,但shadcn/ui的FormField并不支持该prop,这个属性会被透传给内部的react-hook-formController组件,导致Controller在渲染过程中触发父组件LoginForm的状态更新,触发React的跨组件更新警告。 - 登录无提示/无法登录:警告会阻塞正常的状态更新逻辑,同时
startTransition的使用方式可能打乱异步执行时机,加上signIn的跳转行为可能中断客户端错误提示的渲染。
修复步骤
1. 移除FormField上的disabled属性,转移到Input组件
把disabled={isPending}从FormField移到对应的Input上,因为FormField不接收该属性,直接传递会干扰Controller的正常渲染:
// 用户名输入框修改后 <FormField control={form.control} name="username" render={({ field }) => ( <FormItem> <FormControl> <Input placeholder="Username" {...field} disabled={isPending} // 移到Input上 /> </FormControl> <FormMessage /> </FormItem> )} /> // 密码输入框同理修改 <FormField control={form.control} name="password" render={({ field }) => ( <FormItem> <FormControl> <Input type="password" autoComplete="current-password" placeholder="Password" {...field} disabled={isPending} // 移到Input上 /> </FormControl> <FormMessage /> </FormItem> )} />
2. 调整登录逻辑的异步处理
startTransition适用于非紧急状态更新,登录操作属于高优先级逻辑,直接移除它,简化异步处理:
async function onSubmit(values: z.infer<typeof loginFormSchema>) { setError(""); try { const data = await login(values); if (data?.error) { setError(data.error.toString()); } } catch (e) { setError("登录过程中出现未知错误"); } }
3. 优化服务端错误处理(可选)
简化服务端的重复验证(客户端已通过react-hook-form完成校验),确保错误信息准确返回:
"use server" import { z } from "zod"; import { AuthError } from "next-auth"; import { signIn } from "@/auth"; import { DEFAULT_LOGIN_REDIRECT } from "@/routes"; export async function login(values: z.infer<typeof loginFormSchema>) { const { username, password } = values; try { await signIn('credentials', { username, password, redirectTo: DEFAULT_LOGIN_REDIRECT }); return {}; } catch (e) { if (e instanceof AuthError) { switch (e.type) { case "CredentialsSignin": return { error: '无效的用户名或密码!'}; default: return { error: '登录失败,请稍后重试!'}; } } console.error("登录错误:", e); return { error: '登录过程中出现未知错误!'}; } }
4. 修正error状态初始值(可选)
将error初始值设为undefined,避免空字符串可能引发的逻辑歧义:
const [error, setError] = useState<string | undefined>(undefined);
验证效果
完成以上修改后:
- 跨组件更新警告会消失,
Controller渲染过程不再触发父组件状态更新 - 登录失败时错误提示会正常显示,登录成功会正确跳转到目标页面
内容的提问来源于stack exchange,提问作者Paul Serre
相关产品推荐
相关产品推荐

