Unetway

ReactJS - Обработка событий

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

Существуют синтаксические различия:

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

Пример HTML:

<button onclick="activateLasers()">
  Activate Lasers
</button>

 Пример React:

<button onClick={activateLasers}>
  Activate Lasers
</button>

Другое отличие состоит в том, что вы не можете вернуть false, чтобы предотвратить поведение по умолчанию в React. Вы должны вызвать preventDefault явно. Например, с помощью обычного HTML, чтобы предотвратить поведение ссылки по умолчанию при открытии новой страницы, вы можете написать:

<a href="#" onclick="console.log('The link was clicked.'); return false">
  Click me
</a>

В React это могло бы быть:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

Здесь e синтетическое событие. React определяет эти синтетические события в соответствии со спецификацией W3C , поэтому вам не нужно беспокоиться о совместимости между браузерами. 

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

Когда вы определяете компонент с использованием класса ES6 , общий шаблон для обработчика события является методом класса. Например, этот Toggle компонент отображает кнопку, которая позволяет пользователю переключаться между состояниями «ON» и «OFF»:

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // This binding is necessary to make `this` work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);

Вы должны быть осторожны в плане this обратных вызовов JSX. В JavaScript методы класса не связаны по умолчанию. Если вы забудете связать this.handleClick и передать его onClick, this будет undefined когда функция фактически вызвана.

Это не относится к конкретному поведению; это часть того, как функции работают в JavaScript. Как правило, если вы ссылаетесь на метод без (), например onClick={this.handleClick}, вы должны связать этот метод.

Если вызов bind вас раздражает, есть два способа обойти это. Если вы используете синтаксис полей экспериментального открытого класса, вы можете использовать поля классов для правильного привязки обратных вызовов:

class LoggingButton extends React.Component {
  // This syntax ensures `this` is bound within handleClick.
  // Warning: this is *experimental* syntax.
  handleClick = () => {
    console.log('this is:', this);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}

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

class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // This syntax ensures `this` is bound within handleClick
    return (
      <button onClick={(e) => this.handleClick(e)}>
        Click me
      </button>
    );
  }
}

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

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

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