You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js + shadcn UI登录表单报错:渲染时更新组件问题排查

问题排查与修复方案

问题分析

  1. 跨组件更新警告根源:你在FormField组件上直接添加了disabled={isPending}属性,但shadcn/ui的FormField并不支持该prop,这个属性会被透传给内部的react-hook-form Controller组件,导致Controller在渲染过程中触发父组件LoginForm的状态更新,触发React的跨组件更新警告。
  2. 登录无提示/无法登录:警告会阻塞正常的状态更新逻辑,同时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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 03:34:59