Есть ли в React оптимизация навешивания handler на элементы?
Рендеринг и производительность
Да — React не навешивает обработчик на каждый элемент. Используется событийное делегирование: один набор нативных слушателей на корневом контейнере приложения.
Как это работает
{rows.map((row) => (
<tr key={row.id} onClick={() => select(row.id)}>...</tr>
))}
// 10 000 строк ≠ 10 000 addEventListener- При старте React вешает по одному нативному слушателю на каждый тип события на корень приложения (с React 17 — корневой контейнер, раньше — document).
- Клик по
<tr>всплывает до корня; React по target определяет fiber-узел, собирает цепочку React-обработчиков вдоль пути и вызывает их с SyntheticEvent (кросс-браузерная обёртка). onClick={fn}в JSX — не addEventListener, а просто запись функции в props элемента, которую React найдёт при диспатче.
Что это даёт
- Память и скорость монтирования: тысячи элементов списка не требуют тысяч подписок; добавление/удаление узлов не требует управления слушателями.
- Единообразие: одинаковое поведение событий во всех браузерах, автоматическая «отписка» при размонтировании (обработчик — просто проп, исчезает с элементом).
Нюансы
- Инлайн-стрелка
onClick={() => ...}— это новая функция каждый рендер (вопрос мемоизации), но не новая подписка на DOM — подписка всё равно одна на корне. - Несколько типов событий (scroll, некоторые media-события) не делегируются — вешаются напрямую.
- Взаимодействие с нативными addEventListener: нативный слушатель на элементе сработает раньше React-обработчика (событие ещё должно доплыть до корня).
Коротко для собеседования
Да — событийное делегирование: по одному нативному слушателю на тип события на корне приложения; React по всплывшему событию находит компоненты и вызывает их обработчики с SyntheticEvent. Экономит память на больших списках и избавляет от ручного управления подписками; onClick в JSX — проп, а не addEventListener.