表单验证:输出错误消息

时间:2018-05-25 10:37:21

标签: forms reactjs validation jsx

我使用React创建了以下表单并进行了验证:

  • 数据应在输入时验证
  • 数据应在提交前再次验证
  • 所有数据都是强制性的,数据有效

这个程序有效,但我有以下问题:
我使用onBlur监视数据检查,但是当用户在第一个字段中输入无效数据以及第一个字段的错误消息(“Only letters”)时,会显示第二个字段的错误消息(“This field is a需要“)。

如何将我的示例改进为:

  1. 在输入时 - 仅当用户触摸特定字段时才显示错误消息(“需要此字段”或无效数据的特定消息)
  2. 如果按下“提交”按钮 - 则应在所有具有无效数据的字段附近显示错误消息
  3. 我的代码:

    const ErrorOutput = ({ error }) => <span>{error}</span>
    
    class FormItem extends React.Component {
      render() {
        return (
          <div>
            <label>
              {this.props.label}
            </label>
            <input
              {...this.props.input}
            />
            {this.props.error && <ErrorOutput error={this.props.error} />}
          </div>
        );
      }
    }
    
    class App extends React.Component {
      constructor(props){
        super(props)
    
        this.state = {
          firstName: '',
          telNo: '',
          submit: false,
          errors: {
            firstName: '',
            telNo: '',
          },
          invalid: false,
        }
      }
    
      handleSubmit(e){
        e.preventDefault()
        if (this.validate()) {
          console.log('Error!')
        } else {
          console.log('Success!')
        }
      }
    
      validate = () => {
        const { firstName, telNo } = this.state
        const errors = {}
        let invalid = false;
        if (firstName === '') {
          errors.firstName = 'first name is required'
          invalid = true;
        } else if (!firstName.match(/^[a-zA-Z]+$/)) {
          errors.firstName = 'Letters only'
          invalid = true;
        }  
        if (telNo === '') {
          errors.telNo = 'Phone is required'
          invalid = true;
        } else if (!telNo.match(/^[0-9]+$/)) {
          errors.telNo = 'Numbers only'
          invalid = true;
        }
        this.setState({
          invalid,
          errors,
        })
        
        return invalid;
      }
    
      render() {
        return (
          <form onSubmit={this.handleSubmit.bind(this)}>
            <FormItem label='First name:' input={{
                type: 'text',
                name: 'firstName',
                value: this.state.firstName,
                onChange: e => this.setState({ firstName: e.target.value }),
                onBlur: () => this.validate(),
              }} error = {this.state.errors.firstName}
            />
            <FormItem label='Phone number:' input={{
                type: 'tel',
                name: 'telNo',
                value: this.state.telNo,
                onChange: e => this.setState({ telNo: e.target.value }),
                onBlur: () => this.validate(),
              }} error = {this.state.errors.telNo}
            />
            <button>
              Submit
            </button> 
          </form>
        )
      }
    }
    
    ReactDOM.render(
      <App />,
      document.getElementById('root')
    )
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <body>
    <div id="root"></div>
    </body>

3 个答案:

答案 0 :(得分:2)

请从修改后的代码中查看我的示例。我试图简化你的逻辑,使其更具可读性和通用性。

const ErrorOutput = ({ error }) => <span> {error} </span>;

const FormItem = ({ label, input, error }) => (
  <div>
    <label> {label} </label> <input {...input} />
    {error && <ErrorOutput error={error} />}
  </div>
);

class App extends React.Component {
  state = {
    firstName: "",
    telNo: "",
    submit: false,
    errors: {}
  };

  handleSubmit = () => {
    const { firstName, telNo, errors } = this.state;
    const { firstNameError, telNoError } = errors;

    const firstNameIsValid = firstName && !firstNameError;
    const telNoIsValid = telNo && !telNoError;

    firstNameIsValid && telNoIsValid
      ? console.log("Success!")
      : console.log("Error!");
  };

  handleInput = event => {
    this.setState({
      [event.target.name]: event.target.value
    });
  };

  validate = () => {
    const { firstName, telNo } = this.state;

    let errors = {};

    // Name error checking
    switch (true) {
      case !firstName:
        errors.firstNameError = "First name is required";
        break;
      case !firstName.match(/^[a-zA-Z]+$/):
        errors.firstNameError = "First name can have only letters";
        break;
      case firstName.length <= 2:
        errors.firstNameError =
          "First name needs to be at least 2 characters long";
        break;
      default:
        errors.firstNameError = "";
        break;
    }

    switch (true) {
      case !telNo:
        errors.telNoError = "Phone is required";
        break;
      case !telNo.match(/^[0-9]+$/):
        errors.telNoError = "Phone number can have only numbers";
        break;
      case telNo.length <= 8:
        errors.telNoError =
          "Telephone number needs to be at least 8 characters long";
        break;
      default:
        errors.telNoError = "";
        break;
    }

    this.setState({
      errors
    });
  };

  render() {
    const { firstName, telNo, errors } = this.state;
    return (
      <form>
        <FormItem
          label="First name:"
          input={{
            type: "text",
            name: "firstName",
            value: firstName,
            onChange: this.handleInput,
            onBlur: this.validate
          }}
          error={errors.firstNameError}
        />
        <FormItem
          label="Phone number:"
          input={{
            type: "tel",
            name: "telNo",
            value: telNo,
            onChange: this.handleInput,
            onBlur: this.validate
          }}
          error={errors.telNoError}
        />
        <button type="button" onClick={this.handleSubmit}>
          Submit
        </button>
      </form>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

此外,避免在render方法中使用lambda函数以避免性能瓶颈,因为lambdas会在每次渲染时重新生成,并可能导致其他意外的重新渲染。

答案 1 :(得分:1)

问题是,当发生onBlur事件时,你的valitade方法会尝试验证所有字段。要解决此问题,您可以将事件传递给validate方法,然后仅使用

验证引发事件的字段
  

event.target.name

所以你的验证方法看起来像这样

def loop():
    threads = []
    for i in range(1,100):
        ip = '192.168.0.38'
        port = int(i)
        t = Thread(target=scan, args=(ip,int(port)))
        t.start()
        threads.append(t)
    [t.join() for t in threads]

并且您的FormItem看起来像

validate = e => {
  const { firstName, telNo } = this.state
  const errors = {}
  let invalid = false
  if (e && e.target.name == "firstName") {
    if (firstName === "") {
      errors.firstName = "first name is required"
      invalid = true
    } else if (!firstName.match(/^[a-zA-Z]+$/)) {
      errors.firstName = "Letters only"
      invalid = true
    }
  }
  if(e && e.target.name=="telNo"){
  if (telNo === "") {
    errors.telNo = "Phone is required"
    invalid = true
  } else if (!telNo.match(/^[0-9]+$/)) {
    errors.telNo = "Numbers only"
    invalid = true
  }
}
  this.setState({
    invalid,
    errors
  })

  return invalid
}

答案 2 :(得分:1)

您可能需要考虑使用redux-form来管理整个应用程序的表单状态。即使您不选择使用此库,也值得查看Field所包含的输入上定义的meta props,以帮助说明在进行时应考虑的因素。验证表格。