Найкращий спосіб зв’язати обробників подій у React

Зображення: Flickr by liz west

Обв'язування обробників подій у React може бути складним (у вас є JavaScript, щоб подякувати за це). Для тих, хто знає історію Perl і Python, TMTOWTDI (Існує більше, ніж один спосіб зробити це) і TOOWTDI (Є лише один спосіб зробити це) повинні бути знайомими словами. На жаль, JavaScript, принаймні для прив'язки подій, є мовою TMTOWTDI, яка завжди заплутує розробників.

У цій публікації ми вивчимо загальні способи створення прив’язок подій у React, і я покажу їх плюси та мінуси. І найголовніше, я допоможу вам знайти «Тільки один шлях» - або, принаймні, мій улюблений.

Ця публікація передбачає, що ви розумієте необхідність прив’язки, наприклад, чому нам потрібно це зробити.handler.bind (це), або різницю між функцією () {console.log (це); } і () => {console.log (це); }. Якщо ви плутаєтесь у цих питаннях, Саураб Місра виступив із дивовижним повідомленням, де їх пояснювали.

Динамічне прив'язування у візуалізації ()

Перший звичайний випадок - це викликати .bind (це) у функції render (). Наприклад:

клас HelloWorld розширює компонент {
  handleClick (подія) {}
  render () {
    повернути (
      

Привіт, {this.state.name}!

      <кнопка onClick = {this.handleClick.bind (this)}> Клацніть     );   } }

Звичайно, це спрацює. Але подумайте про одне: що станеться, якщо this.state.namechanges?

Ви можете сказати, що зміна this.state.name призведе до повторного відображення компонента (). Добре. Компонент візуалізує оновлення частини імені. Але чи буде виведена кнопка?

Розглянемо той факт, що React використовує Virtual DOM. Коли візуалізація відбудеться, вона порівняє оновлений Virtual DOM з попереднім Virtual DOM, а потім лише оновить змінені елементи до фактичного дерева DOM.

У нашому випадку, коли виклик render () буде викликаний, цей.handleClick.bind (це) буде викликаний також для прив'язки обробника. Цей виклик створить абсолютно новий обробник, який зовсім інший, ніж обробник, який використовується, коли рендер () викликався вперше!

Віртуальний DOM для динамічного прив’язки. Елементи в синьому кольорі будуть відтворені.

Як і у наведеній діаграмі, коли render () викликався раніше, this.handleClick.bind (this) повертає funcA, щоб React знав, що onChange був funcA.

Пізніше, коли render () буде викликаний знову, this.handleClick.bind (this) повертає funcB (зауважте, що він повертає нову функцію щоразу, коли викликається). Таким чином, React знає, що onChange - це вже не funcA, а це означає, що кнопку потрібно повторно відтворити.

Одна кнопка може не бути проблемою. Але що робити, якщо у списку відображено 100 кнопок?

render () {
  повернути (
    {this.state.buttons.map (btn => (
      <кнопка кнопки = {btn.id} onChange = {this.handleClick.bind (this)}>
        {btn.label}
      
    ))}
  );
}

У наведеному вище прикладі будь-яка зміна мітки кнопок призведе до повторного відображення всіх кнопок, оскільки всі кнопки генерують новий обробник onChange.

Прив’язати до конструктора ()

Старий шкільний спосіб - зробити обв’язку в конструкторі. Нічого фантазійного:

клас HelloWorld розширює компонент {
  конструктор () {
    this.handleClick = this.handleClickFunc.bind (це);
  }
  render () {
    повернення (<кнопка onClick = {this.handleClick} />);
  }
}

Цей спосіб набагато кращий за попередній. Виклик візуалізації () не створить новий обробник для onClick, тому <кнопка> не буде відтворена до тих пір, поки кнопка не зміниться.

Віртуальний DOM для прив’язки в конструкторі. Елементи в синьому кольорі будуть відтворені.

Пов’яжіть за допомогою функції стрілки

Завдяки властивостям класу ES7 (на даний момент підтримується Babel) ми можемо робити прив'язки при визначенні методу:

клас HelloWorld розширює компонент {
  handleClick = (подія) => {
    console.log (this.state.name);
  }
  render () {
    повернення (<кнопка onClick = {this.handleClick} />)
  }
}

У наведеному вище коді handleClick - це завдання, еквівалентне:

конструктор () {
  this.handleClick = (подія) => {...} ;
}

Отже, як тільки ініціалізується компонент, this.handleClick ніколи більше не зміниться. Таким чином, це гарантує, що <кнопка> не буде відновлена. Такий підхід є, мабуть, найкращим способом виконання прив’язок. Це просто, легко читати, а головне, це працює.

Динамічне зв’язування за допомогою функції стрілки для декількох елементів

Використовуючи той же фокус функції стрілки, ми можемо використовувати один і той же обробник для декількох входів:

клас HelloWorld розширює компонент {
  handleChange = name => подія => {
    this.setState ({[ім'я]: event.target.value});
  }
  render () {
    повернути (
      
      
    )
  }
}

На перший погляд, це виглядає досить дивовижно завдяки своїй простоті. Однак якщо ви уважно подумаєте, ви виявите, що у нього є та сама проблема, що і в першому підході: щоразу, коли виклик render () буде викликаний, обидва будуть повторно надані.

Дійсно, я вважаю, що цей підхід розумний, і я також не хочу писати кілька ручокXXXChange для кожного поля. На щастя, цей тип "багатокористувальних обробників" рідше з'являється всередині списку. Це означає, що буде лише кілька компонентів , які будуть повторно відтворені, і, ймовірно, не буде проблеми з продуктивністю.

У всякому разі, вигоди, які вона нам приносить, набагато більше, ніж втрати продуктивності. Тому я б запропонував використовувати цей підхід безпосередньо.

У випадку, якщо ці проблеми з продуктивністю стають значущими, я б запропонував кешувати обробники при виконанні прив'язки (але це зробить код менш читабельним):

клас HelloWorld розширює компонент {
  handleChange = name => {
    if (! this.handlers [ім'я]) {
      this.handlers [ім'я] = подія => {
        this.setState ({[ім'я]: event.target.value});
      };
    }
    повернути це.handlers [ім'я];
  }
  render () {
    повернути (
      
      
    )
  }
}

Висновок

Роблячи прив'язки подій у React, ми повинні дуже уважно перевірити, чи динамічно генеруються обробники. Зазвичай це не проблема, коли уражені компоненти з’являються лише один-два рази. Але коли обробники подій з’являються у списку, це може призвести до серйозних проблем з ефективністю.

Рішення

  • Використовуйте прив'язку функції стрілки, коли це можливо
  • Якщо вам потрібно динамічно генерувати прив’язки, розгляньте кешування обробників, якщо прив'язки стануть проблемою продуктивності

Дякуємо за прочитане! Я сподіваюся, що цей пост був корисним. Якщо ви вважаєте цю публікацію корисною, поділіться нею з іншими людьми, рекомендуючи її.

Оновлення:

Омрі Лузон і Шеш згадали лодаш-декораторів та пакети для автоматичного введення реакції для більш зручних прив’язок. Особисто я не є великим прихильником того, щоб автоматично робити що-небудь (я завжди намагаюся мінімізувати такі прив’язки), але автоматичне прив'язування - це абсолютно чудовий спосіб написання чистого коду та заощаджує більше зусиль. Код буде таким:

імпортувати autoBind з 'react-autobind';
клас HelloWorld () {
  конструктор () {
    autoBind (це);
  }
  handleClick () {
    ...
  }
  render () {
    повернення (<кнопка onClick = {this.handleClick} />);
  }
}

Оскільки autoBind буде обробляти прив’язки автоматично, не потрібно використовувати фокус функції стрілки (handleClick = () => {}), щоб робити прив'язку, а у функції render () цю функцію можна використовувати безпосередньо.