СоХабр закрыт.

С 13.05.2019 изменения постов больше не отслеживаются, и новые посты не сохраняются.

| сохранено

H React.js на русском языке. Часть шестая в черновиках



Перевод официальной документации библиотеки React.js на русском языке.

Оглавление:


1 — Часть первая — Установка
2 — Часть вторая — Внедрение JSX
3 — Часть третья — Отрисовка элементов
4 — Часть четвертая — Компоненты и свойства
5 — Часть пятая — Состояние и жизненный цикл
6 — Часть шестая — Обработка событий
7 — Часть седьмая — Условный рендеринг
8 — Часть восьмая — Списки и ключи
9 — Часть девятая (скоро)

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


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

Существует несколько синтаксических различий:

Названия событий React создаются с помощью camelCase, а не lowercase. С JSX вы передаете функцию как обработчик события, а не строку. Например, HTML:

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

В React это выглядит немного по-другому:

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

Еще одно отличие заключается в том, что в React Вы не можете вернуть false к предыдущему состоянию по умолчанию. Явно необходим алгоритм 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 spec поэтому вам не стоит беспокоиться о кросс-браузерной совместимости.

Как правило, при использовании 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')
);

Попробуйте повторить этот пример в CodePen.

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

Такое действие нетипично для React, оно относится к работе функций в JavaScript. В основном, если вы обращаетесь к алгоритму без () после него, например как onClick={this.handleClick}, вы должны связать данный алгоритм.

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

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 App включен по умолчанию. Если вы не используете инициализаторы свойств, вы можете применить стрелочную функцию (arrow function) в обратных сигналов:

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. Чаще всего, это нормальное явление. Однако если обратные сигналы передаются как свойства для нижних компонентов, то такие компоненты могут произвести повторное демонтирование. Чаще всего, мы советуем производить связывание в конструкторе, чтобы избежать подобных проблем с производительностью.

комментарии (14)

+11
lekzd ,  
Скриншот шаблона Wordpress в тему конечно
0
crocodile2u ,  

Еще вариант забиндить this: <button onClick={this.handleClick.bind(this)}>

0
sdwvit ,  
И при каждой отрисовке будет выполняться дорогая функция бинд. Тогда уж в конструктор вынести.
0
murderinc ,  
Еще вариант использовать статические свойства класса https://babeljs.io/blog/2015/06/07/react-on-es6-plus
0
faiwer ,   * (был изменён)

.bind каждый раз порождает новый метод. Это не так дёшево, как может показаться. А ещё если передавать таким вот образом метод внутрь вложенных сложных компонентов, то, если я правильно понимаю это, React будет вынужден вызывать .render() у любого компонента, которому был доступен этот метод (через .props). Т.е. принудительный .render() всем "соучастникам" при каждом .render()-е родительского компонента.


Та же беда и у => функций.

0
Veikedo ,   * (был изменён)

Насколько знаю, при => используется замыкание на this, а не bind.
По крайней мере, в typescript

+1
faiwer ,   * (был изменён)

Принцип работы => не настолько прост, чтобы описать его в двух словах или приравнять к .bind-у. Но в данном случае это не важно, важно то, что любая анонимная функция в методе всякий раз создаётся заново. А это в свою очередь приводит к тому, что .props у дочерних компонентов меняется всегда. И они вынуждены рендериться повторно. Или я что-то путаю? Вроде об этом было даже в документации.

0
Veikedo ,  

Да, верно, спасибо.

0
rogov_dmitry ,  
Ого, большое спасибо за озвучивание этого момента. В голову не приходило такое.

А не ткнете в каком месте в доках об этом говорится?
0
faiwer ,  
The problem with this syntax is that a different callback is created each time the LoggingButton renders. In most cases, this is fine. However, if this callback is passed as a prop to lower components, those components might do an extra re-rendering. We generally recommend binding in the constructor to avoid this sort of performance problem.

https://facebook.github.io/react/docs/handling-events.html

0
koutsenko ,  
Или для простых случаев использовать Stateless Functional Components.
Там this совсем не нужен — область видимости всегда указывает на сам компонент.
0
n0ne ,  
А можно небольшую просьбу? Сделайте оглавление по разделам, а не номерам частей… людям проще ориентироваться будет, куда переходить, если что
Спасибо.
0
foxmuldercp ,   * (был изменён)
Чаще всего, мы советуем производить связывание в конструкторе, чтобы избежать подобных проблем с производительностью.

Эм, а можно пример кода?

+1
html_manpro ,  
constructor(props) {
 super(props);
 this.addTodo = this.addTodo.bind(this);
}


 <button
 className='btn btn-primary'
 onClick={this.addTodo}
 >
Add todo</button>