useDOMEvent
Описание
useDOMEvent — хук для типобезопасного подключения DOM‑событий к элементу. Поддерживает четыре формы вызова: с готовым ref и одним событием, с автосозданием ref и одним событием, с ref и картой событий, а также с картой событий и автосозданием ref.
Хук автоматически использует актуальные обработчики без перевешивания слушателей и аккуратно отсоединяет их при размонтировании. По умолчанию опции слушателя: capture: false, once: false, passive: false.
Сигнатура
// 1) ref + одно событие
function useDOMEvent<El extends Element | null, T extends UseDOMEventType>(
ref: React.RefObject<El | null>,
eventType: T,
eventHandler: UseDOMEventHandler<T>,
eventOptions?: AddEventListenerOptions,
): void;
// 2) одно событие + автoref
function useDOMEvent<El extends Element | null, T extends UseDOMEventType>(
eventType: T,
eventHandler: UseDOMEventHandler<T>,
eventOptions?: AddEventListenerOptions,
): [React.RefObject<El | null>];
// 3) ref + карта событий
function useDOMEvent<El extends Element | null>(
ref: React.RefObject<El | null>,
eventsMap: UseDOMEventMap,
): void;
// 4) карта событий + автoref
function useDOMEvent<El extends Element | null>(
eventsMap: UseDOMEventMap,
): [React.RefObject<El | null>];Параметры
ref— ссылка на DOM‑элемент для навешивания слушателей.eventType— строка DOM‑события (например,'click','keydown').eventHandler— обработчик события; тип события выводится автоматически.eventOptions?— опции слушателя (capture,once,passive).eventsMap— объект вида{ type: handler | [handler, options] }.
Возвращает
- В формах с автoref — кортеж
[ref]. В формах с переданнымref—void.
- В формах с автoref — кортеж
Примеры
1) Существующий ref + одиночное событие
import { useRef } from 'react';
import { useDOMEvent } from '@webeach/react-hooks';
export function InputOnEnter() {
const inputRef = useRef<HTMLInputElement>(null);
useDOMEvent(inputRef, 'keydown', (event) => {
if (event.key === 'Enter') {
console.log('Submit!');
event.preventDefault();
}
});
return <input ref={inputRef} />;
}2) Автосоздание ref + опции слушателя
import { useDOMEvent } from '@webeach/react-hooks';
export function ScrollLogger() {
const [ref] = useDOMEvent<HTMLDivElement>(
'scroll',
(event) => {
console.log('scrollTop =', event.target.scrollTop);
},
{ passive: true },
);
return (
<div ref={ref} style={{ overflow: 'auto', maxHeight: 200 }}>
…content…
</div>
);
}3) Карта событий с разными типами и опциями
import { useRef } from 'react';
import { useDOMEvent } from '@webeach/react-hooks';
export function HoverAndClick() {
const ref = useRef<HTMLButtonElement>(null);
useDOMEvent(ref, {
mouseenter: (event) => console.log('x', event.clientX), // MouseEvent
click: [
(event) => console.log('clicked', event.button),
{ capture: false },
],
});
return <button ref={ref}>Hover or Click</button>;
}Поведение
Подключение в layout‑фазе
- Слушатели навешиваются/снимаются синхронно в layout‑фазе, чтобы не пропускать ранние события перед пейнтом.
Актуальность обработчиков
- Обработчики читаются из «живой» ссылки; вы можете менять функции между рендерами, и это не приведёт к перевешиванию слушателей — будет использована актуальная версия.
Когда перевешиваются слушатели
- Переподключение происходит при смене элемента (
ref.current) или структуры карты событий/их опций (capture/once/passive). Изменение самих функций‑обработчиков не требует перевешивания.
- Переподключение происходит при смене элемента (
Опции по умолчанию
- Если опции не переданы, используются
capture: false,once: false,passive: false.
- Если опции не переданы, используются
Автоочистка
- Все слушатели автоматически снимаются при размонтировании компонента.
SSR‑безопасность
- Во время рендера к
window/documentобращений нет; подписка выполняется в эффекте после маунта.
- Во время рендера к
Когда использовать
- Подключение нативных DOM‑событий к элементам без обёрток (например, кастомные скролл‑контейнеры,
contentEditable). - Сценарии, где обработчик часто меняется и важно не перевешивать слушатели на каждый рендер.
- Когда удобнее описать несколько событий одной картой (одним вызовом).
Когда не использовать
- Если достаточно обычных React‑обработчиков (
onClick,onChange) и не нужны нативные опции слушателя. - Для событий документа/окна можно использовать
refнаdocument.body/window, но часто удобнее специализированные хуки (useWindowEvent, и т.п.).
Частые ошибки
Передача обработчика напрямую как пропса
- Не пишите
onClick={handler}на React‑элементе, ожидая типMouseEventDOM. React‑синтетика и нативные события различаются; данный хук работает с нативными событиями черезaddEventListener.
- Не пишите
Потерянный
refв формах с автoref- Не забудьте привязать возвращённый
refк элементу:<div ref={ref} />. Иначе слушатели не будут навешены.
- Не забудьте привязать возвращённый
Случайная смена опций
- Изменение
capture/once/passiveприводит к перевешиванию слушателей. Убедитесь, что это ожидаемо.
- Изменение
Использование
signal- Параметр
signalв опциях не поддерживается этим API; при необходимости отмены используйте собственную логику.
- Параметр
Типизация
Экспортируемые типы
UseDOMEventHandler<EventType>(event: UseDOMEventInstance<EventType>) => void— типобезопасный обработчик; тип события выводится из имени ('click'→MouseEvent,'keydown'→KeyboardEvent).
UseDOMEventInstance<EventType>- Соответствующий объект события из
GlobalEventHandlersEventMap[EventType].
- Соответствующий объект события из
UseDOMEventMap- Сопоставление
{ [type]: handler | [handler, options] }для навешивания нескольких событий одним вызовом.
- Сопоставление
UseDOMEventOptions- Опции слушателя без
signal:{ capture?: boolean; once?: boolean; passive?: boolean }.
- Опции слушателя без
UseDOMEventType- Объединение всех ключей
GlobalEventHandlersEventMap(например,'click','keydown').
- Объединение всех ключей