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

React表单中Simple Body Validator密码确认验证失效问题排查

React 18 + Firebase 聊天应用注册表单验证问题

我正在开发一个基于React 18和Firebase的聊天应用,在src\pages\Register.jsx组件中,使用Simple Body Validator对表单进行验证,代码如下:

import React, { useState } from "react";
import FormCard from "../components/FormCard/FormCard";
import { make } from "simple-body-validator";

export default function Register() {
  const initialFormData = {
    firstName: "",
    lastName: "",
    email: "",
    password: "",
    password_confirmation: ""
  };

  const validationRules = {
    firstName: ["required", "string", "min:3", "max:255"],
    lastName: ["required", "string", "min:3", "max:255"],
    email: ["required", "email"],
    password: ["required"],
    password_confirmation: ["required"]
  };

  const validator = make(initialFormData, validationRules);
  const [formData, setFormData] = useState(initialFormData);
  const [errors, setErrors] = useState(validator.errors());

  const [pristineFields, setPristineFields] = useState(() =>
    Object.keys(initialFormData)
  );

  const handleChange = (event) => {
    const { name, value } = event.target;
    setFormData((prevFormData) => {
      const newFormData = { ...prevFormData, [name]: value };
      const newPristineFields = pristineFields.filter((f) => f !== name);

      validator.setData(newFormData).validate();
      const validationErrors = validator.errors();
      newPristineFields.forEach((f) => validationErrors.forget(f));
      setErrors(validationErrors);

      setPristineFields(newPristineFields);

      return newFormData;
    });
  };

  const handleSubmit = (event) => {
    event.preventDefault();

    if (!validator.setData(formData).validate()) {
      setErrors(validator.errors());
    }

    setPristineFields([]);
  };

  return (
    <FormCard title="Register">
      <form onSubmit={handleSubmit}>
        <div
          className={`mb-2 form-element ${errors.has("firstName") ? "has-error" : null
            }`}
        >
          <label for="firstName" className="form-label">
            First name
          </label>
          <input
            type="text"
            id="firstName"
            name="firstName"
            value={formData.firstName}
            onChange={handleChange}
            className="form-control form-control-sm"
          />
          {errors.has("firstName") ? (
            <p className="invalid-feedback">{errors.first("firstName")}</p>
          ) : null}
        </div>

        <div
          className={`mb-2 form-element ${errors.has("lastName") ? "has-error" : null
            }`}
        >
          <label for="lastName" className="form-label">
            Last name
          </label>
          <input
            type="text"
            id="lastName"
            name="lastName"
            value={formData.lastName}
            onChange={handleChange}
            className="form-control form-control-sm"
          />
          {errors.has("lastName") ? (
            <p className="invalid-feedback">{errors.first("lastName")}</p>
          ) : null}
        </div>

        <div
          className={`mb-2 form-element ${errors.has("email") ? "has-error" : null
            }`}
        >
          <label for="email" className="form-label">
            Email address
          </label>
          <input
            type="email"
            id="email"
            name="email"
            value={formData.email}
            onChange={handleChange}
            className="form-control form-control-sm"
          />
          {errors.has("email") ? (
            <p className="invalid-feedback">{errors.first("email")}</p>
          ) : null}
        </div>

        <div
          className={`mb-2 form-element ${errors.has("password") ? "has-error" : null
            }`}
        >
          <label for="password" className="form-label">
            Password
          </label>
          <input
            type="password"
            id="password"
            name="password"
            value={formData.password}
            onChange={handleChange}
            className="form-control form-control-sm"
          />
          {errors.has("password") ? (
            <p className="invalid-feedback">{errors.first("password")}</p>
          ) : null}
        </div>

        <div
          className={`mb-2 form-element ${errors.has("password_confirmation") ? "has-error" : null
            }`}
        >
          <label for="password_repeat" className="form-label">
            Confirm Password
          </label>
          <input
            type="password"
            id="password_repeat"
            name="password_confirmation"
            value={formData.password_confirmation}
            onChange={handleChange}
            className="form-control form-control-sm"
          />
          {errors.has("password_confirmation") ? (
            <p className="invalid-feedback">
              {errors.first("password_confirmation")}
            </p>
          ) : null}
        </div>

        <div className="pt-1">
          <button type="submit" className="btn btn-sm btn-success fw-bold">
            Submit
          </button>
        </div>
      </form>
    </FormCard>
  );
}

问题描述

按照我对验证库的理解,password_confirmation字段应该用于确认密码,但当password与password_confirmation内容不匹配(例如密码为"123",确认密码为"1234")时,并未出现验证错误。

疑问

  1. 我哪里操作有误?
  2. 最可靠的修复方法是什么?

问题原因

你当前的验证规则仅给password_confirmation添加了required规则,没有显式添加与password字段匹配的验证规则。Simple Body Validator不会自动识别命名类似字段的关联关系,必须手动指定confirmed规则来实现密码一致性校验。

修复方法

1. 修改验证规则(核心修复)

更新validationRules,给password_confirmation添加confirmed:password规则,同时可以给password增加长度限制(比如min:6)提升安全性:

const validationRules = {
  firstName: ["required", "string", "min:3", "max:255"],
  lastName: ["required", "string", "min:3", "max:255"],
  email: ["required", "email"],
  password: ["required", "min:6"], // 可选:添加密码长度要求
  password_confirmation: ["required", "confirmed:password"] // 关键:添加匹配规则
};

2. 优化实时验证逻辑(可选)

当前handleChange中,修改password时不会重新校验password_confirmation,可以调整逻辑让密码修改时同步触发确认密码的验证:

const handleChange = (event) => {
  const { name, value } = event.target;
  setFormData((prevFormData) => {
    const newFormData = { ...prevFormData, [name]: value };
    let newPristineFields = pristineFields.filter((f) => f !== name);
    // 修改密码时,同时移除确认密码的未修改状态
    if (name === "password") {
      newPristineFields = newPristineFields.filter(f => f !== "password_confirmation");
    }

    validator.setData(newFormData).validate();
    const validationErrors = validator.errors();
    newPristineFields.forEach((f) => validationErrors.forget(f));
    setErrors(validationErrors);

    setPristineFields(newPristineFields);

    return newFormData;
  });
};

这样用户修改密码时,确认密码字段会实时校验并提示不匹配错误。


内容的提问来源于stack exchange,提问作者Razvan Zamfir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 11:54:51