Формы¶
В React HTML-элементы формы ведут себя несколько отлично от остальных DOM-элементов, так как у элементов формы изначально есть внутреннее состояние. К примеру, в эту HTML-форму можно ввести имя:
<form>
<label>
Имя:
<input type="text" name="name" />
</label>
<input type="submit" value="Отправить" />
</form>
По умолчанию браузер переходит на другую страницу при отправке HTML-форм, в том числе и этой. Если вас это устраивает, то не надо ничего менять, в React формы работают как обычно. Однако, чаще всего форму удобнее обрабатывать с помощью JavaScript-функции, у которой есть доступ к введённым данным. Стандартный способ реализации такого поведения называется «управляемые компоненты».
Управляемые компоненты¶
В HTML элементы формы, такие как <input>
, <textarea>
и <select>
, обычно сами управляют своим состоянием и обновляют его когда пользователь вводит данные. В React мутабельное состояние обычно содержится в свойстве компонентов state
и обновляется только через вызов setState()
.
Мы можем скомбинировать оба подхода и сделать состояние React-компонента "единственным источником правды". Тогда React-компонент будет рендерить форму и контролировать её поведение в ответ на пользовательский ввод. Значение элемента формы input в этом случае будет контролировать 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('Отправленное имя: ' + this.state.value)
event.preventDefault()
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Имя:
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Отправить" />
</form>
)
}
}
Мы установили атрибут value
для поля ввода, и теперь в нём всегда будет отображаться значение this.state.value
. Состояние React-компонента стало «источником истины». А так как каждое нажатие клавиши вызывает handleChange
, который обновляет состояние React-компонента, значение в поле будет обновляться по мере того, как пользователь печатает.
В управляемом компоненте с каждой мутацией состояния связана функция-обработчик. Благодаря этому валидация или изменение введённого значения становится простой задачей. Например, если мы хотим, чтобы имя обязательно было набрано заглавными буквами, можно написать такой handleChange
:
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()});
}
Тег textarea¶
HTML-элемент <textarea>
в качестве текста отображает дочерний элемент:
<textarea>
Доброго здоровья, тут просто немного текста внутри тега textarea
</textarea>
В React <textarea>
использует атрибут value
. Таким образом, форму с <textarea>
можно написать почти тем же способом, что и форму с однострочным <input>
:
class EssayForm extends React.Component {
constructor(props) {
super(props)
this.state = {
value:
'Будьте любезны, напишите сочинение о вашем любимом DOM-элементе.',
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(event) {
this.setState({ value: event.target.value })
}
handleSubmit(event) {
alert('Сочинение отправлено: ' + this.state.value)
event.preventDefault()
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Сочинение:
<textarea
value={this.state.value}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Отправить" />
</form>
)
}
}
Обратите внимание, что мы инициализировали this.state.value
в конструкторе, так что в текстовой области изначально есть текст.
Тег select¶
В HTML <select>
создаёт выпадающий список. HTML-код в этом примере создаёт выпадающий список вкусов:
<select>
<option value="grapefruit">Грейпфрут</option>
<option value="lime">Лайм</option>
<option selected value="coconut">Кокос</option>
<option value="mango">Манго</option>
</select>
Пункт списка «Кокос» выбран по умолчанию из-за установленного атрибута selected
. React вместо этого атрибута использует value
в корневом теге select
. В управляемом компоненте так удобнее, потому что обновлять значение нужно только в одном месте (state
). Пример:
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('Ваш любимый вкус: ' + this.state.value)
event.preventDefault()
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Выберите ваш любимый вкус:
<select
value={this.state.value}
onChange={this.handleChange}
>
<option value="grapefruit">Грейпфрут</option>
<option value="lime">Лайм</option>
<option value="coconut">Кокос</option>
<option value="mango">Манго</option>
</select>
</label>
<input type="submit" value="Отправить" />
</form>
)
}
}
Подводя итог, <input type="text">
, <textarea>
, и <select>
работают очень похоже. Все они принимают атрибут value
, который можно использовать, чтобы реализовать управляемый компонент.
Примечание
В атрибут
value
можно передать массив, что позволит выбрать несколько опций в тегеselect
:<select multiple={true} value={['Б', 'В']}>
Загрузка файла¶
В HTML <input type="file">
позволяет пользователю выбрать один или несколько файлов для загрузки с устройства на сервер или управлять им через JavaScript с помощью File API.
<input type="file" />
Так как значение такого элемента доступно только для чтения, это неуправляемый React-компонент. Мы расскажем про этот и другие неуправляемые компоненты далее в документации.
Обработка нескольких элементов input¶
Если вам нужны несколько управляемых элементов 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>
Пойду:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange}
/>
</label>
<br />
<label>
Количество гостей:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange}
/>
</label>
</form>
)
}
}
Обратите внимание, что мы используем вычисляемые имена свойств, чтобы обновить значение в state
через ключ, который соответствует атрибуту name элемента input:
this.setState({
[name]: value,
})
Идентичный ES5-код:
var partialState = {}
partialState[name] = value
this.setState(partialState)
Кроме того, setState()
автоматически производит слияние части состояния с текущим состоянием, то есть нам нужно передать в него только ту часть state
, которую хотим изменить.
Значение null управляемого компонента¶
Если установить управляемому компоненту проп value
, то пользователь не сможет изменить его значение без вашего желания. Если вы установили value
, а поле ввода по-прежнему можно редактировать, то, возможно, вы случайно задали value
, равный undefined
или null
.
Код ниже это демонстрирует. (Изначально заблокированный input
становится редактируемым после небольшой задержки.)
ReactDOM.render(<input value="Привет" />, mountNode)
setTimeout(function () {
ReactDOM.render(<input value={null} />, mountNode)
}, 1000)
Альтернативы управляемым компонентам¶
Использование управляемых компонентов иногда может быть утомительным. Приходится писать обработчик события для каждого варианта изменения ваших данных и проводить всё состояние формы через компонент React. Это может особенно раздражать, если вы переносите существующую кодовую базу в React, или когда работаете над интеграцией React-приложения с другой библиотекой. В такой ситуации могут пригодиться неуправляемые компоненты — альтернативная техника реализации ввода данных в форму.
Полноценные решения¶
Если вы ищите полноценное решение, которое может валидировать ввод, запомнить посещённые поля формы и обработать её отправку, присмотритесь к Formik. Эта библиотека построена на принципах управляемых компонентов и управления состоянием, так что не пренебрегайте их изучением.