2.7 Обработка событий

Обработка событий в React-элементах очень похожа на обработку событий в DOM-элементах. Но есть несколько синтаксических отличий:

  • События React именованы с использованием верблюжьей нотации вместо нижнего регистра.
  • С помощью JSX вы передаете функцию как обработчик события вместо строки

К примеру, данный HTML:


Код
        
  <button onclick="deleteAllUsers()">Удалить всех пользователей</button>
    

в React немного отличается:


Код
        
  <button onClick={deleteAllUsers}>Удалить всех пользователей</button>
    

Другое отличие состоит в том, что вы не можете возвратить false, чтобы предотвратить поведение по умолчанию в React. Вы должны явно вызвать 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>);
    }
  }
    

Посмотреть в CodePen


Вы должны быть внимательны со значением 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>);
    }
  }
    

Этот синтаксис разрешен по умолчанию в Create React App .

Если вы не используете синтаксис инициализатора свойств , вы можете использовать стрелочную функцию в коллбэке:


Код
        
  class Logger extends React.Component {
    onLog () {
      console.log('объект:', this);
    }

    render() {
      //Такой синтаксис гарантирует, что "this" привязан к onLog
      return (<button onClick={(e) => this.onLog(e)}>Лог</button>);
    }
  }
    

Проблема этого синтаксиса в том, что каждый раз, когда отрисовывается Logger, создается новый коллбэк. В большинстве случаев – это нормально. Тем не менее, если этот коллбэк передается как свойство в нижние компоненты, эти компоненты могут выполнять дополнительную перерисовку. В большинстве случаев мы рекомендуем делать привязку в конструкторе или использовать синтаксис инициализатора свойств, чтобы избежать этого вида проблемы производительности.


2.7.1 Передача аргументов в обработчики событий

Внутри цикла обычно требуется передать дополнительный параметр обработчику событий. Например, если 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 любые дальнейшие аргументы передаются в функцию автоматически.

В случае использования инициализатора свойств, можно использовать и такой вариант:


Код
        
 <button onClick={this.deleteRow}>Delete Row</button>
        
    

Данный вариант эквивалентен варианту с bind и имеет самую короткую и читабельную форму записи, но для его корректной работы вам нужно использовать Babel.