React useState 钩子:对象 属性 保持不变

React useState hook: object property remains unchanged

我最近开始学习 React,我有一个有登录表单的组件:

import Joi from "joi";
import { useState } from "react";

const Login = () => {
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const loginSchema = Joi.object().keys({
    username: Joi.string().required(),
    password: Joi.string().required(),
  });
  const [errors, setErrors] = useState({ username: null, password: null });

  const handleSubmit = (form) => {
    form.preventDefault();
    validate();
  };

  const validate = () => {
    const { error } = loginSchema.validate(
      { username, password },
      { abortEarly: false }
    );
    if (!error) return null;
    for (const item of error.details) {
      let key = item.path[0];
      let value = item.message;
      setErrors({ ...errors, [key]: value });
    }
    return errors;
  };

  return (
    <div className="container">
      <div>
        <h1>Login</h1>
      </div>
      <div className="card">
        <div className="card-body">
          <form onSubmit={handleSubmit}>
            <div className="form-group">
              <label htmlFor="username">Username</label>
              <input
                name="username"
                type="text"
                className="form-control"
                id="username"
                placeholder="Enter username"
                value={username}
                onChange={(username) => setUsername(username.target.value)}
              />
              {errors.username && (
                <div className="alert alert-danger">{errors.username}</div>
              )}
            </div>
            <div className="form-group">
              <label htmlFor="password">Password</label>
              <input
                name="password"
                type="password"
                className="form-control"
                id="password"
                placeholder="Password"
                value={password}
                onChange={(password) => setPassword(password.target.value)}
              />
              {errors.password && (
                <div className="alert alert-danger">{errors.password}</div>
              )}
            </div>
            <button type="submit" className="btn btn-primary">
              Submit
            </button>
          </form>
          {/* Delete later */}
          <h4>{JSON.stringify(errors)}</h4>
        </div>
      </div>
    </div>
  );
};

export default Login;

在这里,我试图在错误对象中设置用户名和密码的值,但由于某种原因,只设置了密码 属性,用户名 属性 仍然为空。我已经使用了对象解构,但它仍然没有设置值,请帮助。

我认为问题来自这段代码

for (const item of error.details) {
  let key = item.path[0];
  let value = item.message;
  setErrors({ ...errors, [key]: value });
}

此处您在 for 循环中使用相同的 errors 对象调用 setErrors,因此仅考虑最后一个字段。

这是一个可能的解决方案:

if (!error) return null;

const newErrors = error.details.reduce((acc, item) => {
  const key = item.path[0];
  const value = item.message;
  acc[key] = value;
  return acc;
}, {...errors});

setErrors(newErrors);

状态更新不会立即反映在 React 中,并且由于您遍历数据以多次设置错误对象,由于事件处理程序中的批处理以及状态更新受到影响的事实,它仅在最后一个键中设置通过闭包。

您应该改为创建更新的对象并将其设置为状态一次

   let newErrors = {};
   for (const item of error.details) {
      let key = item.path[0];
      let value = item.message;
      newErrors = { ...newErrors, [key]: value };
    }
  setErrors(prev => ({...prev, ...newErrors}));