Добавить в корзинуПозвонить
Найти в Дзене
Mad Devs

Немного об Event Delegation в React

К сожалению, в нашем современном мире, нет возможности вникать в мелочи и в то, как работают фреймворки.
Мы привыкли к обилию инструментов и тому, как просто с ними оказывается можно работать. А главное, эти инструменты решают наши проблемы. А что еще нужно? Конечно же на этом все и заканчивается. Как мало нужно разработчику для счастья.
Но вот когда что-то реализуешь на чистом JS, начинаешь
Оглавление

К сожалению, в нашем современном мире, нет возможности вникать в мелочи и в то, как работают фреймворки.

Мы привыкли к обилию инструментов и тому, как просто с ними оказывается можно работать. А главное, эти инструменты решают наши проблемы. А что еще нужно? Конечно же на этом все и заканчивается. Как мало нужно разработчику для счастья.

Но вот когда что-то реализуешь на чистом JS, начинаешь задумываться, а как решают те или иные проблемы те самые фреймворки и инструменты.

В этой статье, рассмотрим делегирование событий и для чего это нужно. Как может помочь вам в понимании механизмов работы фреймворка, а возможно даже улучшить ваши решения на чистом JS.

Что такое Event Delegation?

Вы наверное уже сталкивались с делегированием полномочий, задач, заданий. Не правда ли, значение этого слова очень важно. И суть его в централизации принятия решений, тем самым, уменьшая кол-во точек принятия решений. Это работа руководителя.

Пожалуй, ключевые моменты делегирования:

  • Централизация (что дает возможность мониторинга событий)
  • Трекинг (от кого пришло событие)
  • Фильтрация (решаете на что стоит отреагировать, а на что нет)

По сути дела — вы не вешаете событие на каждый дочерний элемент DOM, а ловите их на каком-либо родительском DOM элементе и далее решаете, что и как делать.

Делегирование событий — это метод присоединения обработчиков событий не к элементам, из которых вы на самом деле хотите считывать события, а к элементу более высокого уровня.

Когда стоит применять?

1.В любом случае, когда вы работаете с большим кол-вом интерактивных элементов. Так как элементов много и они могут часто удаляться и добавляться. Не очистили события на элементе, предварительно удалив его из DOM — получили зомби, что могут при достаточно интенсивном удалении и добавлении отъесть память.

2.Решение проблемы срабатывания событий по цепочке в родительских элементах с большой вложенностью дочерних элементов. Например, если кликнуть на самом вложенном дочернем элементе, будут вызваны события click на каждом из родительских элементах. И тут вы можете избежать применения e.stopPropagation() . Вот почему не рекомендуют использовать прерывание цепочки событий в обработчиках, так как если вам понадобится отловить событие на родительском компоненте, то это у вас не получится.

Как применяется Event Delegation в React?

Собственно, наблюдаешь за очень хорошей картиной. Есть уже давно применяемые практики, что действительно решают проблемы, и их применение ты чаще всего видишь в качественных и зарекомендовавших продуктах. Это своего рода паттерны проектирования.

Давайте же посмотрим как React применяет Event Delegation

-2

Для начала откроем Chrome Developer Tools и найдем элемент списка

-3

Выделяем этот элемент и идем в Event Listeners вкладку

-4

Если раскрыть список обработчиков событий click вы заметите, что React-DOM создал два обработчика:

  • Один на нашем выделенном в DOM элементе
  • И еще один на document объекте — глобально

Хм … подумаете вы, так тут на каждом элементе обработчик получается, где же тут Event Delegation ?

Ваш вопрос справедлив, но в каждом деле нужно копать глубже. Давайте копнем глубже и раскроем свойства li элемента.

-5

Что мы видим? Название функции noop как бы намекает, что это заглушка. И что самое главное, эта заглушка существует в единственном, глобальном экземпляре и при удалении элемента в DOM вы легко можете удалить событие element.removeEventListener('click', noop) . Да это немного нивелирует эффект Event Delegation, но если вы немного покопаетесь в интернете, найдете ответ на этот вопрос. Сэкономлю вам немного времени — этот финт связан с багом в iOS Safari.

Немного о баге в iOS Safari

Для каждого так называемого “бага”, который для нас пользователей, таковым является — так как нарушает наш обычный сценарий использования.

Оказывается, что Safari на iPhone не поддерживает делегирование событий для событий щелчка, если только click не происходит по ссылке или текстовому полю.
Тем не менее, изящные инженеры Apple не могли так ошибиться, тут дело в чем-то другом. Должна же быть какая-то причина для такого поведения. Истинная причина не известна, но на вскидку — это возможно проблема управления памятью. По-видимому, наделение всех элементов на странице, возможностью реагировать на нажатия, требует слишком большого количества ресурсов, и инженеры Apple решили отключить его.

Этот факт конечно же является серьезной проблемой для веб страниц с высокой степенью взаимодействия с пользователем.

Это досадная ошибка, но, к счастью, есть обходной путь.

Вы должны сделать элемент кликабельным, предоставив ему собственный обработчик события onclick. Этот обработчик может быть пустым, пока он присутствует, любой элемент будет кликабельным.

document.onclick = function (e) {
//Реакция на клик по e.target
}
div.onclick = function () {} //Пустой обработчик события

Мы по-прежнему обрабатываем событие на уровне документа, но добавляем пустой обработчик события в div, который мы хотим щелкнуть. Теперь неожиданно div становится кликабельным, событие всплывает до документа и обрабатывается правильно.

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

Теперь вы знаете ответ на вопрос: Откуда взялся noop() обработчик у элемента li?

Так же можно заметить упоминание бага сафари в исходных кодах React

-6

Забавный факт, но // TODO: Only do this for the relevant Safaris maybe? намекает на тот факт, что было бы неплохо применять этот трюк только на подверженных багу версиях Safari. Видимо, раз мы видим повсеместную установку noop для тех элементов, что требуют получения click события, значит не добавлено еще механизма для сужения места применения данного трюка.

Этот факт конечно не улучшает настроение разработчику, так как все равно создает нагрузку по использованию памяти на странице, где много компонентов, что предполагают получения фидбека от пользователя в виде клика.

Вернемся к теме Event Delegation в React

Так вот, мы выяснили для чего нужен noop() обработчик события. И теперь можем понять почему событие отлавливается на document глобально.

-7

Ну и самый главный метод, который и является всея Обработчиков Событий Отцом . Тут то и творится магия Event Delegation .

Давайте взглянем на метод обработки событий в исходниках React.

-8

И если углубимся внутрь этого супер-метода в исходниках React, то можно увидеть следующую картину:

-9

Как видите, основная логика по определению элемента, что сгенерировал событие, а так же прокидывание события через архитектуру React, производится ниже.

И если мы глянем, чисто из интереса, на функцию getEventTarget , то увидим, сколько нужно всего учесть, прежде чем выбрать верный элемент, что годится в качестве источника события.

-10

В итоге, за удобство в обработке событий приходится платить более сложной схемой работы кода. Но если просмотреть на тот ад обработчиков, который бы мог поджидать разработчика, то все же Event Delegation рулит. Главное надо понимать общий паттерн, а его применение — дело техники.

Заключение

Прочитав эту статью, вы теперь знаете:

  • Что такое Event Delegation
  • Как он в общих чертах работает в React
  • Поняли для чего нужен noop обработчик события click
  • Узнали о деталях так называемого “бага” в iOS Safari
  • Поняли, что реализация Event Delegation это паттерн централизованной обработки событий.

Надеюсь вы почерпнули, что-то интересное для себя, а если нет, значит возможно закрепили свои знания.

Спасибо за прочтение!

Ранее статья была опубликована тут.