React: Отлавливаем клик вне компонента

  • воскресенье, 28 мая 2017 г. в 23:40:52

В предыдущей статье я рассказал как внедрил Sentry в каждый свой запрос. На данный момент, Sentry работает на клиенсткой и серверной и это радует. По моему мнению, Sentry действительно крутой багтрекер с очень удобным интерфейсом. Сейчас занимаюсь разработкой клиентской части на ReactJS и возникла задача отловить клик вне компонента.

До того, как я решил перейти на React, отлавливал клик с помощью jQuery. Учитывая то, на какой библиотеки я пишу клиентскую, было бы нелогично использовать jQuery. В поисках данного решения, я наткнулся на одну статью Detect a click outside of a React Component (David Hariri).

Решение просто отличное! Однако, единственное что меня смущало, это то, что я использую самописный компонент в котором находится сторонний компонент emoji-mart. Получить ссылку на данный элемент, мне не удавалось через ref.

Основываясь на этом примере, решил немного переделать данный функционал. Не скажу что мое решение элегантней David Hariri, но главное, что оно у меня работало и будет работать в других проектах.

Мое решение:


_23
// Вызывается после удаления компонента из DOM
_23
componentWillUnmount() {
_23
document.removeEventListener('click', this.handleClickOutside, false);
_23
}
_23
_23
// Вызывается до рендера
_23
componentWillMount() {
_23
document.addEventListener('click', this.handleClickOutside, false);
_23
}
_23
_23
// Отлавливаем клик на любую область
_23
handleClickOutside(e) {
_23
// Получаем ссылку на элемент, при клике на который, скрытие не будет происходить
_23
const emojiBlock = document.getElementsByClassName('emoji-mart')[0];
_23
// Проверяем, есть ли в списке родительских или дочерних элементов, вышеуказанный компонент
_23
if (!e.path.includes(emojiBlock)) {
_23
// Если в области кликнутого элемента нету "emojiBlock", то проверяем ниже
_23
// Не произведен ли клик на кнопку, открывающую окно смайлов
_23
const svgSmileBtn = document.querySelector('.chat-input__smile-btn');
_23
// Если клик не производился и на кнопку открытия окна смайлов, то скрываем блок.
_23
if (!e.path.includes(svgSmileBtn)) this.setState({ smilesPopup: false });
_23
}
_23
}

Решение от David Hariri:


_23
// Вызывается после удаления компонента из DOM
_23
componentWillUnmount() {
_23
document.removeEventListener('click', this.handleClickOutside, false);
_23
}
_23
_23
// Вызывается до рендера
_23
componentWillMount() {
_23
document.addEventListener('click', this.handleClickOutside, false);
_23
}
_23
_23
handleClickOutside(event) {
_23
// Получаем элемент, на который произведен клик
_23
const domNode = ReactDOM.findDOMNode(this);
_23
_23
// Проверяем, что элемент присутствует в переменной,
_23
// а так же, является ли "domNode" узел потомком "event.target" узла.
_23
// Если не является, то скрываем элемент.
_23
if ((!domNode || !domNode.contains(event.target))) {
_23
this.setState({
_23
visible : false
_23
});
_23
}
_23
}

#click#outside#component#click outside#trick#snippet