Next.js登录组件表单提交后累计重复渲染问题求助
登录组件重复渲染问题的原因及修复方案
核心原因分析
- 重复绑定表单提交事件:组件每次渲染时都会执行
document.getElementById("form_")?.addEventListener("submit", ...),每渲染一次就新增一个submit事件监听器。后续提交表单时,所有已绑定的监听器都会触发,每次触发又可能引发状态更新,导致重渲染次数随提交次数累计。 - 直接操作DOM违背React数据流:代码中通过
innerHTML直接修改span内容,跳过了React的状态管理机制,这种操作会和React的虚拟DOM更新冲突,进一步加剧渲染异常。 - 状态更新依赖旧状态未用函数式更新:调用
setIsErrExist({ ...isErrExist, status: true })时,依赖闭包中的旧状态值,多次触发状态更新时可能导致状态更新不及时或不准确,引发额外重渲染。
修复后的代码
"use client"; import React, { useState } from "react"; import { z } from "zod"; import styles from "./index.module.scss"; import Link from "next/link"; import { useGetUserMutation } from "@/src/app/store/features/api/apiSlice"; function Login() { console.log("render"); const [formData, setFormData] = useState({ email: "", password: "" }); const [errors, setErrors] = useState({ email: "", password: "" }); const [getUser, { isLoading }] = useGetUserMutation(); const userSchema = z.object({ email: z.string().email("请输入有效的邮箱地址"), password: z .string() .min(6, "密码长度不能少于6位") .max(10, "密码长度不能超过10位"), }); const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); // 清空之前的错误 setErrors({ email: "", password: "" }); const result = userSchema.safeParse(formData); if (!result.success) { const errorObj = result.error.issues.reduce((acc, issue) => { acc[issue.path[0] as keyof typeof errors] = issue.message; return acc; }, {} as typeof errors); setErrors(errorObj); return; } getUser(formData) .unwrap() .then((res) => console.log(res)) .catch((err) => console.log(err)); }; const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { const { name, value } = e.target; setFormData(prev => ({ ...prev, [name]: value })); // 输入时清空对应字段的错误 if (errors[name as keyof typeof errors]) { setErrors(prev => ({ ...prev, [name]: "" })); } }; return ( <form onSubmit={handleSubmit} className={styles.form_}> <h2>Login</h2> <label htmlFor="email_">E-mail Adress:</label> <input type="email" name="email" id="email_" value={formData.email} onChange={handleInputChange} /> {errors.email && <span className={styles.err}>{errors.email}</span>} <label htmlFor="password_">Password:</label> <input type="password" name="password" id="password_" value={formData.password} onChange={handleInputChange} /> {errors.password && <span className={styles.err}>{errors.password}</span>} <button type="submit" disabled={isLoading}> {isLoading ? "Loading..." : "Send"} </button> <Link href="register"> <h4>Go To Register Page</h4> </Link> </form> ); } export default Login;
关键修复说明
- 使用React原生表单绑定:通过
onSubmit直接在form元素上绑定提交事件,避免手动操作DOM添加监听器导致的重复绑定问题。 - 状态管理表单与错误信息:用
formData存储输入值,errors存储错误提示,完全遵循React数据流,不再直接操作DOM。 - 函数式更新状态:更新表单数据时使用
setFormData(prev => ({ ...prev, [name]: value })),确保每次状态更新基于最新值。 - 优化错误处理:通过Zod校验结果生成精准的错误对象,输入时自动清空对应字段的错误提示,提升用户体验。
内容的提问来源于stack exchange,提问作者Hakan Karayılmaz
相关产品推荐
相关产品推荐

