Обробка подій

Обробка подій для React-елементів дуже схожа до обробки подій для DOM-елементів. Але є деякі синтаксичні відмінності:

  • Події React іменуються в camelCase, а не в нижньому регістрі.
  • З JSX ви передаєте функцію як обробник події замість рядка.

Наприклад, HTML:

<button onclick="activateLasers()">
  Активувати лазери
</button>

дещо відрізняється в React:

<button onClick={activateLasers}>
  Активувати лазери
</button>

Інша відмінність полягає в тому, що ви не можете повернути false для того, щоб запобігти поведінці за замовчуванням у React. Ви маєте явно викликати 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, тому вам не потрібно турбуватися про сумісніть між браузерами. Перегляньте довідник по SyntheticEvent, щоб дізнатися більше.

Зазвичай, коли ви використовуєте React, вам не потрібно викликати addEventListener, щоб додати обробник до DOM-елементу після його створення. Натомість, просто вкажіть обробник, коли елемент вперше відрендерився.

Коли ви визначаєте компонент як клас ES6, поширеною практикою є оголошення обробника подій як методу цього класу. Наприклад, цей Toggle компонент рендерить кнопку, котра дозволяє користувачу перемикатись між станами “ON” і “OFF”:

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 ? 'ON' : 'OFF'}
      </button>
    );
  }
}

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

Спробуйте на CodePen

Ви маєте бути обережним із значенням 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={(e) => this.handleClick(e)}>
        Натисни на мене
      </button>
    );
  }
}

Проблема цього синтаксису полягає в тому, що при кожному рендері LoggingButton створюється нова функція зворотнього виклику. У більшості випадків це не створює додаткових проблем. Але, якщо ця функція зворотнього виклику передається в якості пропу в компоненти нижче - вони можуть здійснити додатковий ререндеринг. Як правило, ми рекомендуємо зв’язувати в конструкторі або використувати синтаксис полей класу, щоб уникнути подібних проблем з продуктивністю.

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

Всередині циклу доволі часто потрібно передати додатковий параметр до обробника події. Наприклад, якщо id це ID рядка, можна застосувати один з нижченаведених прикладів:

<button onClick={(e) => this.deleteRow(id, e)}>Видалити рядок</button>
<button onClick={this.deleteRow.bind(this, id)}>Видалити рядок</button>

Обидва приклади є еквівалентними і використовують стрілкові функції та Function.prototype.bind відповідно.

В обох випадках аргумент e, який відповідає події React, буде переданий другим аргументом після ID. Для стрілкової функції ми маємо зробити передачу явною, але для bind будь-які наступні аргументи будуть передані автоматично.