Проверка формы: вывод сообщения об ошибке - PullRequest
0 голосов
/ 25 мая 2018

Я создал следующую форму с проверкой с использованием React:

  • данные должны быть проверены во время ввода
  • данные должны быть проверены еще раз перед отправкой
  • все данные все поля обязательны для заполнения и данные действительны

Эта программа работает, но у меня возникает следующая проблема:
Я проверяю проверку данных с помощью onBlur, но когда пользователь вводит недопустимыйданные в первом поле вместе с сообщением об ошибке для первого поля («Только буквы»), сообщение об ошибке отображается для второго поля («Это поле является обязательным»).

Как можно улучшитьЭто мой пример:

  1. во время ввода - сообщение об ошибке («Это поле обязательно для заполнения» или конкретное сообщение для недопустимых данных) отображалось только в том случае, если пользователь коснулся определенного поля
  2. если была нажата кнопка «Отправить» - тогда сообщения об ошибках должны отображаться рядом со всеми полями с неверными данными

Мой код:

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 голосов
/ 25 мая 2018

Возможно, вы захотите использовать redux-form для управления состоянием формы в вашем приложении.Даже если вы не решите использовать эту библиотеку, стоит взглянуть на мета-реквизиты , которые определены на входах, обернутых Field, чтобы помочь уточнить, какие соображения следует учитывать при проверке форм.

0 голосов
/ 25 мая 2018

Пожалуйста, проверьте мой пример из вашего модифицированного кода.Я попытался немного упростить вашу логику и сделать ее более читабельной и универсальной.

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>

Кроме того, избегайте использования лямбда-функций в методе рендеринга, чтобы избежать узких мест в производительности, поскольку лямбда-выражения генерируются при каждом рендеринге и могут вызывать дополнительные непреднамеренные повторные рендеринг.

0 голосов
/ 25 мая 2018

Проблема в том, что когда происходит событие onBlur, ваш метод valitade пытается проверить все поля.Чтобы решить эту проблему, вы можете передать событие методу validate, а затем только проверить поле, вызвавшее событие, используя

event.target.name

такваш метод проверки будет выглядеть примерно так

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
}

, а ваш FormItem будет выглядеть как

<FormItem label='First name:' input={{
            type: 'text',
            name: 'firstName',
            value: this.state.firstName,
            onChange: e => this.setState({ firstName: e.target.value }),
            onBlur: (e) => this.validate(e),
          }} error = {this.state.errors.firstName}
        /> 
...