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

Обробка подій для 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, тому вам не потрібно турбуватися про сумісніть між браузерами. React-події працюють інакше, ніж нативні події. Перегляньте довідник по 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={() => this.handleClick()}>        Натисни на мене
      </button>
    );
  }
}

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

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

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