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

Этот хук должен работать так.

Простой случай

Во-первых, нам нужен хук реакции под названием useRef. В общем случае этот хук позволяет нам что-то запомнить между рендерами компонентов. Мы можем поместить что-то в ref.current, и это значение не будет меняться между рендерами. И очень часто этот хук используется для сохранения ссылки на компонент. Мы можем присвоить значение хука свойству ref, и когда компонент визуализируется, containerRef.current является ссылкой на элемент HTML.

Давайте создадим простой щелчок вне пользовательского хука.

Для его создания мы используем хук реакции под названием useEffect. Этот хук используется для вставки возможно эффективного кода в компонент.

Сначала мы создаем функцию прослушивания событий. В этих функциях мы проверяем, что ссылка на компонент пуста или нет. Если компонент отображается, он не пустой. Затем мы проверяем, содержит ли компонент цель события. Если не содержит, мы вызываем обратный вызов с событием.

Затем мы добавляем прослушиватель событий в document. Мы будем наблюдать за событием mousedown и touchstart.

Чтобы удалить прослушиватели событий, нам нужно вернуть функцию «очистки» из хука useEffect.

И это все. Мы можем использовать этот крючок. Но в простом случае.

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

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

Для проверки нам нужно получить координаты компонента в документе.

Для этого используем getBoundingClientRect.

Сначала рассчитываем размер компонента. И нам нужно сохранить его между рендерами. Для этого мы можем использовать хук useRef.

Далее нам нужна функция для проверки координат цели события клика внутри компонента.

В click listener получаем MouseEvent. Это событие содержит координаты события клика (pageX и pageY).

Итак, у нас есть координата клика и координаты прямоугольника компонента.

Далее нам нужна функция для вычисления находится точка внутри прямоугольника или нет.

Эта функция довольно проста.

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

И затем мы меняем нашу функцию слушателя следующим образом.

Но это касается MouseEvent. Свойства TouchEvent pageX и pageY. Для получения координат касания мы можем использовать свойство touches. Чтобы обрабатывать координаты как для мыши, так и для сенсорного события, давайте создадим простую функцию.

И теперь функция слушателя

Вот и все. Ниже приведен полный список хуков useOnClickOutside.