2.7 Обработка событий
Обработка событий в React-элементах очень похожа на обработку событий в DOM-элементах. Но есть несколько синтаксических отличий:
- События React именованы с использованием верблюжьей нотации вместо нижнего регистра.
- С помощью JSX в качестве обработчика события вы передаете функцию, а не строку
К примеру, данный HTML:
<button onclick="deleteAllUsers()">Удалить всех пользователей</button>
в React немного отличается:
<button onClick={deleteAllUsers}>Удалить всех пользователей</button>
Другое отличие состоит в том, что в React вы не можете возвратить false
, чтобы
предотвратить поведение по умолчанию. Вы должны явно вызывать preventDefault()
.
К примеру, для нативного HTML, чтобы предотвратить поведение ссылки по умолчанию – открытие новой страницы, вы можете написать:
<a href="#" onclick="console.log('Пользователь был удален.'); return false">
Удалить пользователя
</a>
В React это будет выглядеть так:
function DeleteUserLink() {
function onClick(e) {
e.preventDefault();
console.log('Пользователь был удален.');
}
return (
<a href="#" onClick={onClick}>Удалить пользователя</a>
);
}
Здесь e
– это синтетическое событие. React определяет такие синтетические события в
соответствии со спецификацией W3C. Поэтому о кросс-браузерной
совместимости переживать не стоит. Изучите справочное руководство
SyntheticEvent, чтобы узнать больше.
Как правило, в React не нужно вызывать addEventListener
, чтобы
добавить слушателей в DOM-элемент, после того как он был создан. Вместо этого, просто передайте слушатель
элементу в методе отрисовки.
Когда вы определяете компонент, используя ES6-класс, общий паттерн таков: обработчик события
должен быть методом класса. К примеру, наш компонент Conditioner
отрисовывает
кнопки button
, которые позволяют пользователю регулировать текущую температуру:
class Conditioner extends React.Component {
constructor(props) {
super(props);
this.state = {temperature: 0};
// Привязка необходима, чтобы сделать this доступным в коллбэке
this.onIncrease = this.onIncrease.bind(this);
this.onDecrease = this.onDecrease.bind(this);
}
onIncrease(){
this.setState(prevState => ({
temperature: prevState.temperature + 1
}))
}
onDecrease(){
this.setState(prevState => ({
temperature: prevState.temperature - 1
}))
}
render() {
return (
<p>
<h2>Текущая температура: {this.state.temperature}</h2>
<button onClick={this.onDecrease}>-</button>
<button onClick={this.onIncrease}>+</button>
</p>
);
}
}
Вы должны быть внимательны со значением this
в JSX-коллбэках. В JavaScript, методы класса не
привязаны по умолчанию. Если вы забудете привязать функцию this.onIncrease
и передать
её в onClick
, то, когда эта функция будет вызвана, this
будет undefined
.
Это неспецифическое поведение для React. Это тот случай, когда функции работают как в
JavaScript. Как правило, если вы ссылаетесь на метод без ()
после него,
например, onClick={this.onIncrease}
, вам необходимо привязать этот метод.
Если синтаксис привязки вас раздражает, есть два способа, как это обойти. Первый способ: использовать экспериментальный синтаксис инициализатора свойств, помогающий правильно привязывать коллбэки:
class Logger extends React.Component {
//Такой синтаксис гарантирует, что "this" привязан к onLog
//Внимание! это экспериментальный синтаксис!
onLog = () => {
console.log('объект:', this);
}
render() {
return (<button onClick={this.onLog}>Лог</button>);
}
}
Этот синтаксис разрешен по умолчанию в .
Если вы не используете синтаксис инициализатора свойств, есть второй способ: передавать стрелочную функцию-коллбэк как свойство элемента:
class Logger extends React.Component {
onLog () {
console.log('объект:', this);
}
render() {
//Такой синтаксис гарантирует, что "this" привязан к onLog
return (<button onClick={(e) => this.onLog(e)}>Лог</button>);
}
}
Однако у этого синтаксиса есть одна проблема: при каждой отрисовке Logger
, создается
новый коллбэк. В большинстве случаев – всё будет в порядке. Тем не менее, если этот коллбэк передается как
свойство в нижние компоненты, последние могут выполнять дополнительную перерисовку, что может снизить
производительность. Чтобы этого избежать мы рекомендуем делать привязку в конструкторе или
использовать синтаксис инициализатора свойств.
Часто внутри цикла обработчику событий нужно передать дополнительный параметр.
Например, если id
является идентификатором строки, рабочими будут следующие варианты:
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
Две вышеуказанные строки эквивалентны. Первая использует стрелочную функцию, а вторая
Function.prototype.bind
.
В обоих случаях аргумент e
, представляющий событие React, будет передан как
второй аргумент после id
. В стрелочной функции мы должны передавать его явно,
а с bind
любые дальнейшие аргументы передаются в функцию автоматически.