Обработка событий
Обработка событий в React-элементах очень похожа на обработку событий в DOM-элементах. Но есть несколько синтаксических отличий:
- События в React именуются в стиле camelCase вместо нижнего регистра.
- С JSX вы передаёте функцию как обработчик события вместо строки.
Например, в HTML:
<button onclick="activateLasers()">
Активировать лазеры
</button>
В React немного иначе:
<button onClick={activateLasers}> Активировать лазеры
</button>
Ещё одно отличие — в React нельзя предотвратить обработчик события по умолчанию, вернув false
. Нужно явно вызвать preventDefault
. Например, в обычном HTML, чтобы отменить выполнение встроенного обработчика события у ссылки, которое открывает новую страницу, можно написать:
<a href="#" onclick="console.log('По ссылке кликнули.'); return false">
Нажми на меня
</a>
В React это будет выглядеть так:
function ActionLink() {
function handleClick(e) { e.preventDefault(); console.log('По ссылке кликнули.'); }
return (
<a href="#" onClick={handleClick}> Нажми на меня
</a>
);
}
В приведённом выше коде e
— это синтетическое событие. React определяет синтетические события в соответствии со спецификацией W3C, поэтому не волнуйтесь о кроссбраузерности. События React работают не совсем как нативные. Изучите руководство о SyntheticEvent
, чтобы узнать о них больше.
При использовании React обычно не нужно вызывать addEventListener
, чтобы добавить обработчики в DOM-элемент после его создания. Вместо этого добавьте обработчик сразу после того, как элемент отрендерился.
В компоненте, определённом с помощью ES6-класса, в качестве обработчика события обычно выступает один из методов класса. Например, этот компонент Toggle
рендерит кнопку, которая позволяет пользователю переключать состояния между «Включено» и «Выключено»:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// Эта привязка обязательна для работы `this` в колбэке. this.handleClick = this.handleClick.bind(this); }
handleClick() { this.setState(state => ({ isToggleOn: !state.isToggleOn })); }
render() {
return (
<button onClick={this.handleClick}> {this.state.isToggleOn ? 'Включено' : 'Выключено'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
При обращении к this
в JSX-колбэках необходимо учитывать, что методы класса в JavaScript по умолчанию не привязаны к контексту. Если вы забудете привязать метод this.handleClick
и передать его в onClick
, значение this
будет undefined
в момент вызова функции.
Дело не в работе React, это часть того, как работают функции в JavaScript. Обычно, если ссылаться на метод без ()
после него, например, onClick={this.handleClick}
, этот метод нужно привязать.
Если вам не по душе bind
, существует два других способа. Если вы пользуетесь экспериментальным синтаксисом общедоступных полей классов, вы можете использовать его, чтобы правильно привязать колбэки:
class LoggingButton extends React.Component {
// Такой синтаксис гарантирует, что `this` привязан к handleClick. // Предупреждение: это экспериментальный синтаксис handleClick = () => { console.log('значение this:', this); }
render() {
return (
<button onClick={this.handleClick}>
Нажми на меня
</button>
);
}
}
Такой синтаксис доступен в Create React App по умолчанию.
Если вы не пользуетесь синтаксисом полей, можете попробовать стрелочные функции в колбэке:
class LoggingButton extends React.Component {
handleClick() {
console.log('значение this:', this);
}
render() {
// Такой синтаксис гарантирует, что `this` привязан к handleClick. return ( <button onClick={() => this.handleClick()}> Нажми на меня
</button>
);
}
}
Проблема этого синтаксиса в том, что при каждом рендере LoggingButton
создаётся новый колбэк. Чаще всего это не страшно. Однако, если этот колбэк попадает как проп в дочерние компоненты, эти компоненты могут быть отрендерены снова. Мы рекомендуем делать привязку в конструкторе или использовать синтаксис полей классов, чтобы избежать проблем с производительностью.
Передача аргументов в обработчики событий
Внутри цикла часто нужно передать дополнительный аргумент в обработчик события. Например, если id
— это идентификатор строки, можно использовать следующие варианты:
<button onClick={(e) => this.deleteRow(id, e)}>Удалить строку</button>
<button onClick={this.deleteRow.bind(this, id)}>Удалить строку</button>
Две строки выше — эквивалентны, и используют стрелочные функции и Function.prototype.bind
соответственно.
В обоих случаях аргумент e
, представляющий событие React, будет передан как второй аргумент после идентификатора. Используя стрелочную функцию, необходимо передавать аргумент явно, но с bind
любые последующие аргументы передаются автоматически.