Элементы формы HTML работают немного иначе, чем другие элементы DOM в React, потому что элементы формы естественным образом сохраняют некоторое внутреннее состояние. Например, эта форма в обычном HTML допускает одно имя:

<form>
  <label>
    Name:
    <input type="text" name="name" />
  </label>
  <input type="submit" value="Submit" />
</form>

Эта форма имеет поведение HTML-формы по умолчанию для просмотра на новой странице, когда пользователь отправляет форму. Если вы хотите, чтобы это было в React, это просто работает. Но в большинстве случаев удобно иметь функцию JavaScript, которая обрабатывает представление формы и имеет доступ к данным, которые пользователь вводил в форму. Стандартный способ достичь этого - с помощью технологии под названием «контролируемые компоненты».

Контролируемые компоненты

В HTML элементы формы , такие как <input>, <textarea>и как <select>правило , ведут свое собственное состояние и обновлять на основе пользовательского ввода. В React изменчивое состояние обычно сохраняется в состоянии свойства компонентов и обновляется только с помощью setState().

Мы можем объединить их, сделав Реагированное состояние «единственным источником истины». Затем компонент React, который отображает форму, также контролирует, что происходит в этой форме при последующем вводе пользователя. Элемент входной формы, значение которого контролируется React таким образом, называется «управляемым компонентом».

Например, если мы хотим, чтобы предыдущий пример регистрировал имя при его отправке, мы можем написать форму как контролируемый компонент:

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

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

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

Поскольку value атрибут установлен в нашем элементе формы, отображаемое значение всегда будет this.state.value, в результате чего React будет источником истины. Так как handleChange выполняется каждое нажатие клавиши для обновления состояния React, отображаемое значение будет обновляться по мере ввода пользователем.

С управляемым компонентом каждая мутация состояния будет иметь связанную функцию обработчика. Это упрощает изменение или проверку ввода пользователя. Например, если мы хотим, чтобы эти имена записывались со всеми заглавными буквами, мы могли бы написать handleChange:

handleChange(event) {
  this.setState({value: event.target.value.toUpperCase()});
}

Тег textarea

В HTML <textarea>элемент определяет его текст своими дочерними элементами:

<textarea>
  Hello there, this is some text in a text area
</textarea>

В React <textarea> используется value вместо этого атрибут. Таким образом, форма с использованием a <textarea> может быть написана очень аналогично форме, которая использует однострочный вход:

class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 'Please write an essay about your favorite DOM element.'
    };

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

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('An essay was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Essay:
          <textarea value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

Обратите внимание, что this.state.value инициализируется в конструкторе, так что текстовая область начинается с некоторого текста.

Тег select

В HTML <select> создается раскрывающийся список. Например, этот HTML создает раскрывающийся список вкусов:

<select>
  <option value="grapefruit">Grapefruit</option>
  <option value="lime">Lime</option>
  <option selected value="coconut">Coconut</option>
  <option value="mango">Mango</option>
</select>

Обратите внимание, что параметр Coconut изначально выбран из-за selected атрибута. Вместо использования этого selected атрибута вместо этого используется value атрибут в корневом select теге. Это более удобно в управляемом компоненте, потому что вам нужно только обновить его в одном месте. Например:

class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'coconut'};

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

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('Your favorite flavor is: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Pick your favorite La Croix flavor:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">Grapefruit</option>
            <option value="lime">Lime</option>
            <option value="coconut">Coconut</option>
            <option value="mango">Mango</option>
          </select>
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

В целом, это делает его так , что <input type="text">, <textarea> и <select> все работают очень похожи, - все они принимают value атрибут, который можно использовать для реализации контролируемого компонента.

Заметка

Вы можете передать массив в value атрибут, позволяя вам выбрать несколько параметров в select теге:

<select multiple={true} value={['B', 'C']}>

Тег file

В HTML <input type="file"> позволяет пользователю выбрать один или несколько файлов из своего хранилища устройств для загрузки на сервер или манипулировать JavaScript через File API.

<input type="file" />

Поскольку его значение доступно только для чтения, это неконтролируемый компонент в React. 

Обработка нескольких входов

Когда вам нужно обрабатывать несколько управляемых input элементов, вы можете добавить name атрибут к каждому элементу и позволить функции обработчика выбирать, что делать, исходя из значения event.target.name.

Например:

class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

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

  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  render() {
    return (
      <form>
        <label>
          Is going:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          Number of guests:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}

Обратите внимание, как мы использовали синтаксис имени вычисленного имени ES6 для обновления ключа состояния, соответствующего данному имени ввода:

this.setState({
  [name]: value
});

Это эквивалентно этому ES5-коду:

var partialState = {};
partialState[name] = value;
this.setState(partialState);

Кроме того, поскольку setState() автоматически объединяет частичное состояние в текущее состояние , нам нужно было только позвонить ему с измененными частями.

Контролируемое входное значение Null

Указание поддержки значения на управляемом компоненте не позволяет пользователю изменять ввод, если вы этого не хотите. Если вы указали , value но вход по - прежнему доступны для редактирования, вы можете случайно установить value на undefined или null.

Следующий код демонстрирует это. (Сначала вход заблокирован, но становится доступным после короткой задержки.)

ReactDOM.render(<input value="hi" />, mountNode);

setTimeout(function() {
  ReactDOM.render(<input value={null} />, mountNode);
}, 1000);

Альтернативы контролируемым компонентам

Иногда бывает сложно использовать контролируемые компоненты, потому что вам нужно написать обработчик событий для каждого способа, которым ваши данные могут измениться, и передать все входное состояние через компонент React. Это может стать особенно раздражающим, когда вы конвертируете существующую базу кода в React или интегрируете приложение React с библиотекой, отличной от React. В таких ситуациях вы можете проверить неконтролируемые компоненты , альтернативный метод для реализации входных форм.