繁体   English   中英

如何向我的 React 组件中的表单添加验证?

[英]How do I add validation to the form in my React component?

我的联系页面表单如下,

<form name="contactform" onSubmit={this.contactSubmit.bind(this)}>
  <div className="col-md-6">
    <fieldset>
      <input ref="name" type="text" size="30" placeholder="Name"/>
      <br/>
      <input refs="email" type="text" size="30" placeholder="Email"/>
      <br/>
      <input refs="phone" type="text" size="30" placeholder="Phone"/>
      <br/>
      <input refs="address" type="text" size="30" placeholder="Address"/>
      <br/>
    </fieldset>
  </div>
  <div className="col-md-6">
    <fieldset>
      <textarea refs="message" cols="40" rows="20"
                className="comments" placeholder="Message"/>
    </fieldset>
  </div>
  <div className="col-md-12">
    <fieldset>
      <button className="btn btn-lg pro" id="submit"
              value="Submit">Send Message</button>
    </fieldset>
  </div>
</form>

需要为所有字段添加验证。 谁能帮我在这个反应表单中添加验证?

您应该避免使用 refs,您可以使用onChange函数来完成。

在每次更改时,更新已更改字段的状态。

然后您可以轻松检查该字段是否为空或您想要的任何其他内容。

您可以执行以下操作:

    class Test extends React.Component {
        constructor(props){
           super(props);
      
           this.state = {
               fields: {},
               errors: {}
           }
        }
    
        handleValidation(){
            let fields = this.state.fields;
            let errors = {};
            let formIsValid = true;

            //Name
            if(!fields["name"]){
               formIsValid = false;
               errors["name"] = "Cannot be empty";
            }
      
            if(typeof fields["name"] !== "undefined"){
               if(!fields["name"].match(/^[a-zA-Z]+$/)){
                  formIsValid = false;
                  errors["name"] = "Only letters";
               }        
            }
       
            //Email
            if(!fields["email"]){
               formIsValid = false;
               errors["email"] = "Cannot be empty";
            }
      
            if(typeof fields["email"] !== "undefined"){
               let lastAtPos = fields["email"].lastIndexOf('@');
               let lastDotPos = fields["email"].lastIndexOf('.');

               if (!(lastAtPos < lastDotPos && lastAtPos > 0 && fields["email"].indexOf('@@') == -1 && lastDotPos > 2 && (fields["email"].length - lastDotPos) > 2)) {
                  formIsValid = false;
                  errors["email"] = "Email is not valid";
                }
           }  

           this.setState({errors: errors});
           return formIsValid;
       }
        
       contactSubmit(e){
            e.preventDefault();

            if(this.handleValidation()){
               alert("Form submitted");
            }else{
               alert("Form has errors.")
            }
      
        }
    
        handleChange(field, e){         
            let fields = this.state.fields;
            fields[field] = e.target.value;        
            this.setState({fields});
        }
    
        render(){
            return (
                <div>           
                   <form name="contactform" className="contactform" onSubmit= {this.contactSubmit.bind(this)}>
                        <div className="col-md-6">
                          <fieldset>
                               <input ref="name" type="text" size="30" placeholder="Name" onChange={this.handleChange.bind(this, "name")} value={this.state.fields["name"]}/>
                               <span style={{color: "red"}}>{this.state.errors["name"]}</span>
                              <br/>
                             <input refs="email" type="text" size="30" placeholder="Email" onChange={this.handleChange.bind(this, "email")} value={this.state.fields["email"]}/>
                             <span style={{color: "red"}}>{this.state.errors["email"]}</span>
                             <br/>
                             <input refs="phone" type="text" size="30" placeholder="Phone" onChange={this.handleChange.bind(this, "phone")} value={this.state.fields["phone"]}/>
                             <br/>
                             <input refs="address" type="text" size="30" placeholder="Address" onChange={this.handleChange.bind(this, "address")} value={this.state.fields["address"]}/>
                             <br/>
                         </fieldset>
                      </div>
          
                  </form>
                </div>
          )
        }
    }

    React.render(<Test />, document.getElementById('container'));

在此示例中,我仅对电子邮件和姓名进行了验证,但您知道如何进行验证。 其余的你可以自己做。

也许有更好的方法,但你会明白的。

这里是小提琴。

希望这可以帮助。

我已经采用了您的代码并使用库react-form-with-constraints 对其进行了调整https : //codepen.io/tkrotoff/pen/LLraZp

const {
  FormWithConstraints,
  FieldFeedbacks,
  FieldFeedback
} = ReactFormWithConstraints;

class Form extends React.Component {
  handleChange = e => {
    this.form.validateFields(e.target);
  }

  contactSubmit = e => {
    e.preventDefault();

    this.form.validateFields();

    if (!this.form.isValid()) {
      console.log('form is invalid: do not submit');
    } else {
      console.log('form is valid: submit');
    }
  }

  render() {
    return (
      <FormWithConstraints
        ref={form => this.form = form}
        onSubmit={this.contactSubmit}
        noValidate>

        <div className="col-md-6">
          <input name="name" size="30" placeholder="Name"
                 required onChange={this.handleChange}
                 className="form-control" />
          <FieldFeedbacks for="name">
            <FieldFeedback when="*" />
          </FieldFeedbacks>

          <input type="email" name="email" size="30" placeholder="Email"
                 required onChange={this.handleChange}
                 className="form-control" />
          <FieldFeedbacks for="email">
            <FieldFeedback when="*" />
          </FieldFeedbacks>

          <input name="phone" size="30" placeholder="Phone"
                 required onChange={this.handleChange}
                 className="form-control" />
          <FieldFeedbacks for="phone">
            <FieldFeedback when="*" />
          </FieldFeedbacks>

          <input name="address" size="30" placeholder="Address"
                 required onChange={this.handleChange}
                 className="form-control" />
          <FieldFeedbacks for="address">
            <FieldFeedback when="*" />
          </FieldFeedbacks>
        </div>

        <div className="col-md-6">
          <textarea name="comments" cols="40" rows="20" placeholder="Message"
                    required minLength={5} maxLength={50}
                    onChange={this.handleChange}
                    className="form-control" />
          <FieldFeedbacks for="comments">
            <FieldFeedback when="*" />
          </FieldFeedbacks>
        </div>

        <div className="col-md-12">
          <button className="btn btn-lg btn-primary">Send Message</button>
        </div>
      </FormWithConstraints>
    );
  }
}

截屏:

表单验证截图

这是一个快速的黑客。 如需适当的演示,请查看https://github.com/tkrotoff/react-form-with-constraints#examples

import React from 'react';
import {sendFormData} from '../services/';

class Signup extends React.Component{
  constructor(props){
    super(props);
     this.state = {
       isDisabled:true
     }                                                                                                 
     this.submitForm = this.submitForm.bind(this);
  }
  validateEmail(email){
   const pattern = /[a-zA-Z0-9]+[\.]?([a-zA-Z0-9]+)?[\@][a-z]{3,9}[\.][a-z]{2,5}/g;
   const result = pattern.test(email);
   if(result===true){
     this.setState({
       emailError:false,
       email:email
     })
   } else{
     this.setState({
       emailError:true
     })
   }
 }
 handleChange(e){
  const target = e.target;
  const value = target.type === 'checkbox' ? target.checked : target.value;
  const name = target.name;
  this.setState({
    [name]: value
  });
  if(e.target.name==='firstname'){
    if(e.target.value==='' || e.target.value===null ){
      this.setState({
        firstnameError:true
      })
    } else {
      this.setState({
        firstnameError:false,     
        firstName:e.target.value
      })
    }
  }
  if(e.target.name==='lastname'){
    if(e.target.value==='' || e.target.value===null){
      this.setState({
        lastnameError:true
      })
    } else {
      this.setState({
        lastnameError:false,
        lastName:e.target.value
      })
    }
  }
  if(e.target.name==='email'){
   this.validateEmail(e.target.value);
  }
  if(e.target.name==='password'){
    if(e.target.value==='' || e.target.value===null){
      this.setState({
        passwordError:true
      })
    } else {
      this.setState({
        passwordError:false,
        password:e.target.value
      })
    }
 }
 if(this.state.firstnameError===false && this.state.lastnameError===false && 
  this.state.emailError===false && this.state.passwordError===false){
    this.setState({
      isDisabled:false
    })
 }
}
submitForm(e){
  e.preventDefault();
  const data = {
   firstName: this.state.firstName,
   lastName: this.state.lastName,
   email: this.state.email,
   password: this.state.password
  }
  sendFormData(data).then(res=>{
    if(res.status===200){
      alert(res.data);
      this.props.history.push('/');
    }else{

    } 
  });
 }
render(){
return(
  <div className="container">
    <div className="card card-login mx-auto mt-5">
      <div className="card-header">Register here</div>
        <div className="card-body">
            <form id="signup-form">
              <div className="form-group">
                <div className="form-label-group">
                  <input type="text" id="firstname" name="firstname" className="form-control" placeholder="Enter firstname" onChange={(e)=>{this.handleChange(e)}} />
                  <label htmlFor="firstname">firstname</label>
                  {this.state.firstnameError ? <span style={{color: "red"}}>Please Enter some value</span> : ''} 
                </div>
              </div>
              <div className="form-group">
                <div className="form-label-group">
                  <input type="text" id="lastname" name="lastname" className="form-control" placeholder="Enter lastname" onChange={(e)=>{this.handleChange(e)}} />
                  <label htmlFor="lastname">lastname</label>
                  {this.state.lastnameError ? <span style={{color: "red"}}>Please Enter some value</span> : ''}
                </div>
              </div>
              <div className="form-group">
                <div className="form-label-group">
                  <input type="email" id="email" name="email" className="form-control" placeholder="Enter your email" onChange={(e)=>{this.handleChange(e)}} />
                  <label htmlFor="email">email</label>
                  {this.state.emailError ? <span style={{color: "red"}}>Please Enter valid email address</span> : ''}
                </div>
              </div>                
              <div className="form-group">
                <div className="form-label-group">
                  <input type="password" id="password" name="password" className="form-control" placeholder="Password" onChange={(e)=>{this.handleChange(e)}} />
                  <label htmlFor="password">Password</label>
                  {this.state.passwordError ? <span style={{color: "red"}}>Please enter some   value</span> : ''}
                </div>
              </div>                
              <button className="btn btn-primary btn-block" disabled={this.state.isDisabled} onClick={this.submitForm}>Signup</button>
            </form>
        </div>
      </div>
    </div>
  );
 }
}
export default Signup;

试试这个,例如,下面输入标签中的 required 属性将确保 name 字段不应该被提交为空。

<input type="text" placeholder="Your Name" required />

使用 React Hook,表单变得非常简单(React Hook 表单: https : //github.com/bluebill1049/react-hook-form

我重用了你的 html 标记。

import React from "react";
import useForm from 'react-hook-form';

function Test() {
  const { useForm, register } = useForm();
  const contactSubmit = data => {
    console.log(data);
  };

  return (
    <form name="contactform" onSubmit={contactSubmit}>
      <div className="col-md-6">
        <fieldset>
          <input name="name" type="text" size="30" placeholder="Name" ref={register} />
          <br />
          <input name="email" type="text" size="30" placeholder="Email" ref={register} />
          <br />
          <input name="phone" type="text" size="30" placeholder="Phone" ref={register} />
          <br />
          <input name="address" type="text" size="30" placeholder="Address" ref={register} />
          <br />
        </fieldset>
      </div>
      <div className="col-md-6">
        <fieldset>
          <textarea name="message" cols="40" rows="20" className="comments" placeholder="Message" ref={register} />
        </fieldset>
      </div>
      <div className="col-md-12">
        <fieldset>
          <button className="btn btn-lg pro" id="submit" value="Submit">
            Send Message
          </button>
        </fieldset>
      </div>
    </form>
  );
}

2022年
React 建议使用 3 种方法来处理表单:

  1. 受控组件- 在 HTML 中,<input>、<textarea> 和 <select> 等表单元素通常会维护自己的状态并根据用户输入更新状态。 在 React 中,可变状态通常保存在组件的状态属性中,并且仅通过 setState() 更新。 我们可以通过使 React 状态成为“单一事实来源”来将两者结合起来。 然后呈现表单的 React 组件还控制在后续用户输入时该表单中发生的事情。 以这种方式由 React 控制其值的输入表单元素称为“受控组件”。

  2. 不受控制的组件- 使用受控组件有时会很乏味,因为您需要为数据可以更改的每种方式编写一个事件处理程序,并通过 React 组件传输所有输入状态。 当您将预先存在的代码库转换为 React,或将 React 应用程序与非 React 库集成时,这会变得特别烦人。 在这些情况下,您可能想要检查不受控制的组件,这是实现输入表单的替代技术。

  3. 成熟的解决方案——如果您正在寻找一个完整的解决方案,包括验证、跟踪访问的字段和处理表单提交,Formik 是最受欢迎的选择之一。 然而,它建立在受控组件和管理状态的相同原则之上——所以不要忽视学习它们。

所有方法都适用于反应钩子。
首先考虑哪个组件更适合您的需求,并使用其适当的验证解决方案。

更干净的方法是使用 joi-browser 包。 在状态中,您应该拥有包含表单中所有错误的错误对象。 最初它应该被设置为一个空对象。 创建模式;

import Joi from "joi-browser";
schema = {
    username: Joi.string()
      .required()
      .label("Username")
      .email(),
    password: Joi.string()
      .required()
      .label("Password")
      .min(8)
      .regex(/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,1024}$/) //special/number/capital
   };

然后使用架构验证表单:

validate = () => {
    const options = { abortEarly: false };
    const result = Joi.validate(this.state.data, this.schema, options);
    console.log(data) // always analyze your data
    if (!result.error) return null; 
    const errors = {};
    for (let item of result.error.details) errors[item.path[0]] = item.message; //in details array, there are 2 properties,path and message.path is the name of the input, message is the error message for that input.
    return errors;
  };

在提交表格之前,请检查表格:

handleSubmit = e => {
    e.preventDefault();
    const errors = this.validate(); //will return an object
    console.log(errors);
    this.setState({ errors: errors || {} }); //in line 9 if we return {}, we dont need {} here
    if (errors) return;
    //so we dont need to call the server
    alert("success");
    //if there is no error call the server
    this.dosubmit();
  };

我们有很多选项来验证 react js 表单。 也许 npm 包有一些自己的限制。 根据您的需求,您可以选择正确的验证器包。 我想推荐一些,下面列出了这些。

如果有人知道比这更好的解决方案,请将其放在评论部分以供其他人参考。

可能会迟到 - 如果您不想大量修改当前代码并且仍然能够在整个项目中使用类似的验证代码,您也可以尝试这个 - https://github.com/vishalvisd/反应验证器

试试powerform-react 它基于powerform ,这是一个超级便携的 Javascript 表单库。 一旦学会,它就可以在任何框架中使用。 它甚至适用于 vanilla Javascript。

查看这个使用powerform-react 的简单表单

还有一个复杂的例子

尝试使用这种方法https://medium.com/@krzakmarek88/complex-form-validation-in-react-hooks-cb07367196b9这是发达国家在阵营鱼钩但是当你需要它可以适应以简单的方式作出反应

假设你知道 react useState Hook ,如果你的表单很简单,你可以使用state变量来保存每个输入字段的值。 然后在每个输入字段上添加onChange处理函数,这将更新状态变量。 最后,您可以检查存储在状态变量中的值,以确保所有输入字段都有某个值。 这是一个简单的例子。

import { useState } from "react";

export default function App() {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const onChangeHandler = (fieldName, value)=>{
    if(fieldName === "name"){
      setName(value);
    }
    else if(fieldName==="email"){
      setEmail(value);
    }
  }
  const onSubmitHandler = (e)=>{
    e.preventDefault();
    if(name.trim()==="" || email.trim() ==""){
      alert("required both field");
    }
    else{
      alert(name+" " +email);
      setName("");
      setEmail("");
    }
  }
  return (
    <div className="App">
      <form onSubmit={(e)=>{onSubmitHandler(e)}}>
        <input type="text" value={name} onChange={(e)=>{ onChangeHandler("name",e.target.value)}} /> <br/>
         <input type="email"  value={email} onChange={(e)=>{ onChangeHandler("email",e.target.value)}} /> <br/>
         <input type="submit" value="Submit" />
        </form>
    </div>
  );
}

但是,如果您有一个复杂的表单,则很难将每个值保存在状态变量中,然后在每个字段上使用验证。 对于复杂的表单,建议使用Formik ,它可以为您做所有事情,您可以使用Yup验证包,它也受Formik支持,它允许您添加的不仅仅是简单的验证。

在您的表单中试用此验证插件,您可以在其中添加自定义验证规则。

创建组件 FormValidation.js

  import { useState } from "react";

  const FormValidation = ({ validationRules, formInput }) => {
    const [errors, setErrors] = useState(null);

    const validation = () => {
      // define a empty object to store errors.
      let allErrors = {};
      // Run loop on validation object
      Object.keys(validationRules).forEach((name) => {
        // name is the name of input field
        const rulesArr = validationRules[name];

        // Run loop on validation array applied on that input
        rulesArr.forEach((rule) => {
          // Skip if any error message is already stored in allErrors object
          if (!allErrors[name]) {
            let result;
            // If rule is an array than it is a type of a function with parameter
            switch (Array.isArray(rule)) {
              case true: {
                // take the function name and parameter value from rule array
                const [functionName, paramValue] = rule;
                // call validation function

                result = functionName(formInput, name, paramValue);
                break;
              }

              default:
                // call validation function
                result = rule(formInput, name);
                break;
            }
            if (result) {
              // append error in object
              allErrors = { ...allErrors, ...result };
            }
          }
        });
      });

      return allErrors;
    };

    const validate = () =>
      new Promise((resolve, reject) => {
        const errorObj = validation();

        if (Object.keys(errorObj).length === 0) {
          setErrors({});
          resolve("Success");
        } else {
          setErrors(errorObj);
          reject(Error("Some Error Occurred"));
        }
      });

    return { validate, errors, setErrors };
  };

  export const required = (formInputs, inputName) =>
    !formInputs[inputName] && { [inputName]: "This field is required" };

  function emailPattern(email) {
    return String(email)
      .toLowerCase()
      .match(
        /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
      );
  }

  export const email = (formInputs, inputName) =>
    !emailPattern(formInputs[inputName]) && {
      [inputName]: "Please enter valid email",
    };

  export function passwordPattern(formInputs, inputName) {
    const value = formInputs[inputName];

    let error;
    if (value.length < 8) {
      error = "Your password must be at least 8 characters";
    }
    if (value.search(/[a-z]/i) < 0) {
      error = "Your password must contain at least one letter.";
    }
    if (value.search(/[0-9]/) < 0) {
      error = "Your password must contain at least one digit.";
    }

    if (value.search(/[ `!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/) < 0) {
      error = "Your password must contain at least one special character.";
    }

    return (
      error && {
        [inputName]: error,
      }
    );
  }

  export const maxLength = (formInputs, inputName, paramValue) =>
    formInputs[inputName].length > paramValue && {
      [inputName]: `Maximum characters are ${paramValue}`,
    };

  export const minLength = (formInputs, inputName, paramValue) =>
    formInputs[inputName].length < paramValue && {
      [inputName]: `Minimum characters are ${paramValue}`,
    };

  export default FormValidation;

我想在我的 Login.js 中实施验证

    import React, { useState } from "react";
    import { Button, Form } from "react-bootstrap";
    import FormValidation, {
      required,
      email,
      passwordPattern,
    } from "utils/FormValidation";

    const Login = () => {
      const [formInput, setFormInput] = useState({
        email: "",
        password: "",
      });

      const { validate, errors, setErrors } = FormValidation({
        validationRules: {
          email: [required, email],
          password: [required, passwordPattern],
        },
        formInput,
      });

      const handleChange = (e) => {
        setFormInput({ ...formInput, [e.target.name]: e.target.value });
      };

      const handleSubmit = (e) => {
        e.preventDefault();
        validate().then(() => {
          //do whatever you want
          console.log(formInput);
          // you can set server error manually
          setErrors({ email: "Email already exist" });
        });
      };

      return (
        <section className="gradient-form" style={{ backgroundColor: "#eee" }}>
          <div className="container py-5 h-100">
            <div className="row d-flex justify-content-center align-items-center h-100">
              <div className="col-xl-10">
                <div className="card rounded-3 text-black">
                  <div className="row g-0">
                    <div className="col-lg-6">
                      <div className="card-body p-md-5 mx-md-4">
                        <Form onSubmit={handleSubmit}>
                          <p>Please login to your account</p>
                          <Form.Group className="mb-3" controlId="formBasicEmail">
                            <Form.Control
                              type="text"
                              name="email"
                              placeholder="Enter email"
                              onChange={handleChange}
                            />
                            {errors?.email && <span>{errors.email}</span>}
                          </Form.Group>

                          <Form.Group
                            className="mb-3"
                            controlId="formBasicPassword"
                          >
                            <Form.Control
                              type="password"
                              name="password"
                              placeholder="Password"
                              onChange={handleChange}
                            />
                            {errors?.password && <span>{errors.password}</span>}
                          </Form.Group>
                          <Form.Group
                            className="mb-3"
                            controlId="formBasicCheckbox"
                          >
                            <Form.Check type="checkbox" label="Check me out" />
                          </Form.Group>
                          <div className="d-grid gap-2 mb-3">
                            <Button
                              variant="primary"
                              type="submit"
                              className="gradient-custom-2"
                              size="md"
                            >
                              Submit
                            </Button>
                          </div>
                        </Form>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </section>
      );
    };
    export default Login;

现在您可以在验证数组中传递任何自定义函数:

const { validate, errors, setErrors } = FormValidation({
    validationRules: {
      email: [required, email],
      password: [required, passwordPattern, customFunciton],
    },
    formInput,
  });

const customFunciton = (formInputs, inputName) => ({
[inputName]: `This error is from my custom function`,
});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM