简体   繁体   中英

How to validate Form in React by pressing submit button

i am learning React and working on my pet-project. So, i have a form with custom input components and I want to validate them only when the user clicks on the button, but I don't know how. Full input check is whether the form is not empty and filled out correctly. I tried to create a model for subscribing components to the button click event, but when the form was filled correctly, the states were updated not after 1, but after 2 clicks, and I don't know why.

Screen of form in browser:

Code of Form component:

import React from 'react';
import { FormBox } from '../FormBox';
import styles from './checkoutpage.module.scss'

export function CheckoutPage() {
  return (
    <div className={styles.container}>
      <div className={styles.formContainer}>
        <h2>checkout</h2>
          <legend>Contact info</legend>
          <FormBox
            type='email'
            placeholder='Email address'
            errorMessage='Enter a valid email address'
          />
          <legend>Shipping info</legend>
          <FormBox
            type='name'
            placeholder='Name'
          />
          <FormBox
            type='text'
            placeholder='Streer address'
          />
          <FormBox
            type='text'
            placeholder='Apt / Suite / Other (optional)'
          />
          <div className={styles.divideBox}>
            <FormBox
              type='text'
              placeholder='City'
            />
            <FormBox
              type='text'
              placeholder='Province'
            />
          </div>
          <div className={styles.divideBox}>
            <FormBox
              type='text'
              placeholder='Postal Code'
            />
            <FormBox
              type='text'
              placeholder='Country'
            />
          </div>
          <FormBox
            type='text'
            placeholder='Phone Number'
          />
      </div>
    </div>
  );
}

Code of Input component:

import React, { useEffect, useRef, useState } from "react";
import styles from "./formbox.module.scss";

interface IFormBox {
  type: string;
  placeholder: string;
  errorMessage?: string;
}

export function FormBox({ type, placeholder, errorMessage }: IFormBox) {
  const [valid, setValid] = useState(true);
  const ref = useRef<HTMLInputElement>(null);

  const checkValidityChange = () => {
    if (ref.current) {
      setValid(ref.current.checkValidity());
    }
  }

  useEffect(() => {
    if (ref.current) {
      if (valid && ref.current.value !== "") {
        ref.current.style.backgroundColor = "#e8f0fe";
      }
      else {
        ref.current.style.backgroundColor = "white";
      }
    }
  }, [valid])

  return (
    <div className={styles.container}>
      <input 
        ref={ref}
        onChange={checkValidityChange}
        name="formInput" type={type}
        placeholder={placeholder}
        onBlur={() => {if (ref.current && ref.current.value === "") ref.current.style.backgroundColor = "white";}}
      />

      {!valid && errorMessage && (
        <span>{'* '.concat(errorMessage)}</span>
      )}
    </div>
  );
}

Method i tried to use:

Form component:

import React, { useEffect, useRef, useState } from 'react';
import { FormBox } from '../FormBox';
import styles from './checkoutpage.module.scss'

export function CheckoutPage() {
  const [formValidity, setFormValidity] = useState(false);

  const [emailValidity, setEmailValidity] = useState(false);
  const [nameValidity, setNameValidity] = useState(false);
  const [addressValidity, setAddressValidity] = useState(false);
  const [aptValidity, setAptValidity] = useState(false);
  const [cityCalidity, setCityValidity] = useState(false);
  const [provinceValidity, setProvinceValidity] = useState(false);
  const [codeValidity, setCodeValidity] = useState(false);
  const [countryValidity, setCountryValidity] = useState(false);
  const [phoneValidity, setPhoneValidity] = useState(false);

  const refButton = useRef<HTMLButtonElement>(null);

  function subscrition (event: MouseEvent, setValidity: (v: boolean) => void, setValidityValue: boolean) {
    if (event.target === refButton.current) {
      setValidity(setValidityValue);
    }
  }

  const getValidity = (e: React.MouseEvent<HTMLButtonElement>) => {
    e.preventDefault()
    const result = emailValidity && nameValidity && addressValidity && aptValidity && cityCalidity && provinceValidity && codeValidity && countryValidity && phoneValidity

    setFormValidity(result)
    console.log(formValidity)
  }

  return (
    <div className={styles.container}>
      <form className={styles.formContainer}>
        <h2>checkout</h2>
          <legend>Contact info</legend>
          <FormBox
            subscrition={subscrition}
            type='email'
            placeholder='Email address'
            errorMessage='Enter a valid email address'
            setFullValidity={setEmailValidity}
          />
          <legend>Shipping info</legend>
          <FormBox
            subscrition={subscrition}
            type='name'
            placeholder='Name'
            setFullValidity={setNameValidity}
          />
          <FormBox
            subscrition={subscrition}
            type='text'
            placeholder='Streer address'
            setFullValidity={setAddressValidity}
          />
          <FormBox
            subscrition={subscrition}
            type='text'
            placeholder='Apt / Suite / Other (optional)'
            setFullValidity={setAptValidity}
          />
          <div className={styles.divideBox}>
            <FormBox
              subscrition={subscrition}
              type='text'
              placeholder='City'
              setFullValidity={setCityValidity}
            />
            <FormBox
              subscrition={subscrition}
              type='text'
              placeholder='Province'
              setFullValidity={setProvinceValidity}
            />
          </div>
          <div className={styles.divideBox}>
            <FormBox
              subscrition={subscrition}
              type='text'
              placeholder='Postal Code'
              setFullValidity={setCodeValidity}
            />
            <FormBox
              subscrition={subscrition}
              type='text'
              placeholder='Country'
              setFullValidity={setCountryValidity}
            />
          </div>
          <FormBox
            subscrition={subscrition}
            type='text'
            placeholder='Phone Number'
            setFullValidity={setPhoneValidity}
          />
          <button ref={refButton} onClick={getValidity} type="submit" className={styles.btn}>Place Your Order</button>
          {formValidity && (
            <span style={{backgroundColor: "green"}}>Form is valid</span>
          )}
          {!formValidity && (
            <span style={{backgroundColor: "red"}}>Form is invalid</span>
          )}
      </form>
    </div>
  );
}

Input component:

import React, { useEffect, useRef, useState } from "react";
import styles from "./formbox.module.scss";

interface IFormBox {
  type: string;
  placeholder: string;
  errorMessage?: string;
  setFullValidity: (v: boolean) => void;
  subscrition: (event: MouseEvent, setValidity: (v: boolean) => void, setValidityValue: boolean) => void;
}

export function FormBox({ type, placeholder, errorMessage, setFullValidity, subscrition }: IFormBox) {
  const [valid, setValid] = useState(true);
  const ref = useRef<HTMLInputElement>(null);

  const checkValidityChange = () => {
    if (ref.current) {
      setValid(ref.current.checkValidity());
    }
  }

  useEffect(() => {
    if (ref.current) {
      // @ts-ignore
      document.addEventListener('click', (event: MouseEvent) => {subscrition(event, setFullValidity, valid && (ref.current.value !== ""))})
    }
  })

  useEffect(() => {
    if (ref.current) {
      if (valid && ref.current.value !== "") {
        ref.current.style.backgroundColor = "#e8f0fe";
      } else {
        ref.current.style.backgroundColor = "white";
      }
    }
  }, [valid])

  return (
    <div className={styles.container}>
      <input 
        ref={ref}
        onChange={checkValidityChange}
        name="formInput" type={type}
        placeholder={placeholder}
        onBlur={() => {
          if (ref.current && ref.current.value === "") {
            ref.current.style.backgroundColor = "white";
          }
        }}
      />

      {!valid && errorMessage && (
        <span>{'* '.concat(errorMessage)}</span>
      )}
    </div>
  );
}

Any advice on how to improve the code will be very warmly welcomed. Thanks for reading, and sorry if i waste your time.

You can follow this example to validate input fields. Here input reference is used like ref to validate data.

WORKING DEMO

编辑#SO-material-onclick-edit

import React from "react";
import ReactDOM from "react-dom";

import "./style.css";

function validate(name, email, password) {
  // we are going to store errors for all fields
  // in a signle array
  const errors = [];

  if (name.length === 0) {
    errors.push("Name can't be empty");
  }

  if (email.length < 5) {
    errors.push("Email should be at least 5 charcters long");
  }
  if (email.split("").filter(x => x === "@").length !== 1) {
    errors.push("Email should contain a @");
  }
  if (email.indexOf(".") === -1) {
    errors.push("Email should contain at least one dot");
  }

  if (password.length < 6) {
    errors.push("Password should be at least 6 characters long");
  }

  return errors;
}

class SignUpForm extends React.Component {
  constructor() {
    super();
    this.state = {
      errors: []
    };

    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(e) {
    e.preventDefault();

    const name = ReactDOM.findDOMNode(this._nameInput).value;
    const email = ReactDOM.findDOMNode(this._emailInput).value;
    const password = ReactDOM.findDOMNode(this._passwordInput).value;

    const errors = validate(name, email, password);
    if (errors.length > 0) {
      this.setState({ errors });
      return;
    }

    // submit the data...
  }

  render() {
    const { errors } = this.state;
    return (
      <form onSubmit={this.handleSubmit}>
        {errors.map(error => (
          <p key={error}>Error: {error}</p>
        ))}
        <input
          ref={nameInput => (this._nameInput = nameInput)}
          type="text"
          placeholder="Name"
        />
        <input
          ref={emailInput => (this._emailInput = emailInput)}
          type="text"
          placeholder="Email"
        />
        <input
          ref={passwordInput => (this._passwordInput = passwordInput)}
          type="password"
          placeholder="Password"
        />

        <button type="submit">Submit</button>
      </form>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<SignUpForm />, rootElement);

More Details

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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