Фото Ника Фьюингса на Unsplash
Если вы участвовали в проектах на JavaScript в качестве фронтенд/бекенд разработчика, то вы должны быть хорошо знакомы с прикреплением событий к документу, элементу или объекту окна через встроенный модуль событий.
React создает уровень абстракции над основным модулем JavaScript, чтобы предоставить оболочку событий (Synthetic Event wrapper), с помощью которой можно получить доступ к элементам Virtual DOM и манипулировать ими. В большинстве случаев это все, что нужно для передачи функции одному из Synthetic Event обработчиков.
Статья поможет понять:
- как правильно добавлять, очищать и отслеживать слушателей событий;
- как строить стратегии производительности и оптимизации слушателей;
- зачем необходимо создавать пользовательские event listener;
- для чего использовать традиционные и “реактивные” реализации.
Прежде, чем погрузиться в код и реализацию, давайте проясним процесс добавления событий в React и JavaScript. Мы можем добавить слушателя в объекты окна, документы или элемент в коде.
В любом случае лучше прикреплять таких слушателей на этапе монтирования компонента во избежание любого неожиданного поведения.
Вот как это делается в функциональном компоненте:
Очень важно очистить и удалить слушателя после того, как он перестанет быть нужен, а это произойдет, когда компонент размонтируется. Если мы оставим слушателей болтаться без очистки, могут возникнуть серьезные проблемы с производительностью приложения.
Еще один способ убедиться, что слушатели находятся в нужных местах или успешно удалены – проверять их и контролировать. Сделать это можно в Chrome Dev Tools :
Когда мы открываем инструменты разработчика и переходим к некоему элементу (в нашем случае – к документу) можно видеть, что EventListener прикреплен к DOM, ожидая выполнения.
Поскольку в статье используется codesandbox , можно заметить дополнительных слушателей в списке ниже. Так все выглядело бы во вкладке Event Listeners при наличии нескольких слушателей на объектах window, document и element:
Подключено несколько слушателей
Если внимательно посмотреть на последний скриншот, можно увидеть события “ contextmenu ” и “ auxclick ”, добавленные из codesandbox в глобальное пространство имен событий.
Еще раз подчеркнем, что во избежание каких-либо проблем со слушателями событий нужно правильно добавлять (на этапе монтажа компонента) и очищать их.
Когда мы понимаем, как наблюдать за слушателями, следует убедиться, что они настроены на оптимальную производительность.
В методе addEventListener есть третий аргумент, позволяющий указывать дополнительные параметры, такие, как свойство once:
Свойство указывает, что listener должен быть вызван не более одного раза после добавления. Если оно принимает логическое значение true, то listener будет автоматически удален при вызове. В документации говорится, что опция автоматически удаляется после запуска слушателя, поэтому не нужно беспокоиться о ее очистке.
Еще один очень удобный флаг – passive option. С его помощью можно значительно улучшить производительность listener и сократить время отклика элементов. Вот как это выглядит:
Логическое значение true сообщает, что указанная слушателем функция никогда не вызовет функцию preventDefault () . Если пассивный слушатель вызывает функцию preventDefault (), агент пользователя создаст предупреждение в консоли. Дополнительные сведения для изучения ключей можно найти в разделе улучшение производительности .
В качестве эксперимента автор проверяет производительность слушателя с опцией и без. При обычном слушателе время выполнения составляет примерно 164мс, а с passive option, время выполнения упало до 116мс – на 30% эффективнее.
React предоставляет нам набор событий, но иногда может понадобиться более гибкое решение: обработать сложную анимацию, пользовательское скролл-событие, вызывающее изменения UI, или попытаться избежать перекрытия событий click в глобальном пространстве имен.
Мы всегда можем создать собственные события:
Разберем , что здесь происходит .
- Мы ожидаем, что в метод createCustomEvent будут переданы два параметра: имя события и любые дополнительные данные, находящиеся в параметре detail.
- Внутри тела метода мы проверяем, существует ли объект window. Если нет, событие не может быть присоединено, и мы вернем null. В ином случае вернем конструктор CustomEvent, создающий пользовательское событие и передающий любые данные внутрь объекта detail.
Большая часть работы сделана, но нам нужен еще один метод, который отправит созданное событие:
Теперь рассмотрим эту часть кода:
- Во-первых, скрипт ожидает получения параметра из toggleSideDrawerHandler, который позже будет передан с вызовом события.
- Во-вторых, вызывается метод createCustomEvent, которому передаются дополнительные данные и имя события.
- И последнее, созданное событие отправляется в toggleDrawer всякий раз, когда будет вызван этот метод.
Когда методы событий созданы, осталось использовать их в действии. Можно прикрепить метод toggleSideDrawerHandler к любому элементу, а он вызовет toggleDrawer . В песочнице находится весь код с рабочей реализацией рассмотренного примера.
Использование слушателей событий в React – не то, чем можно регулярно злоупотреблять. Лучше использовать предоставляемый React по умолчанию Synthetic Event.
Представим ситуацию, когда необходимо вызвать action на «другой стороне» приложения, а передача нашего метода невозможна или реализация глобального состояния была бы излишней. В этом случае использование Event Listener можно считать правильным выходом из ситуации.