В предыдущей статье я рассказал как внедрил Sentry в каждый свой запрос. На данный момент, Sentry работает на клиенсткой и серверной и это радует. По моему мнению, Sentry действительно крутой багтрекер с очень удобным интерфейсом. Сейчас занимаюсь разработкой клиентской части на ReactJS и возникла задача отловить клик вне компонента.
До того, как я решил перейти на React, отлавливал клик с помощью jQuery. Учитывая то, на какой библиотеки я пишу клиентскую, было бы нелогично использовать jQuery. В поисках данного решения, я наткнулся на одну статью Detect a click outside of a React Component (David Hariri).
Решение просто отличное! Однако, единственное что меня смущало, это то, что я использую самописный компонент в котором находится сторонний компонент emoji-mart. Получить ссылку на данный элемент, мне не удавалось через ref
.
Основываясь на этом примере, решил немного переделать данный функционал. Не скажу что мое решение элегантней David Hariri, но главное, что оно у меня работало и будет работать в других проектах.
Мое решение:
_23// Вызывается после удаления компонента из DOM_23componentWillUnmount() {_23 document.removeEventListener('click', this.handleClickOutside, false);_23}_23_23// Вызывается до рендера_23componentWillMount() {_23 document.addEventListener('click', this.handleClickOutside, false);_23}_23_23// Отлавливаем клик на любую область_23handleClickOutside(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_23componentWillUnmount() {_23 document.removeEventListener('click', this.handleClickOutside, false);_23}_23_23// Вызывается до рендера_23componentWillMount() {_23 document.addEventListener('click', this.handleClickOutside, false);_23}_23_23handleClickOutside(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}