В предыдущей статье я рассказал как внедрил 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}