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

Дата: 29.05.2017 в 02:40, Категория: ReactJS
  • 3580
  • 10
React: Отлавливаем клик вне компонента

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

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

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

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

Мое решение:

// Вызывается после удаления компонента из DOM
componentWillUnmount() {
  document.removeEventListener('click', this.handleClickOutside, false);
}

// Вызывается до рендера
componentWillMount() {
  document.addEventListener('click', this.handleClickOutside, false);
}

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

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

// Вызывается после удаления компонента из DOM
componentWillUnmount() {
  document.removeEventListener('click', this.handleClickOutside, false);
}

// Вызывается до рендера
componentWillMount() {
  document.addEventListener('click', this.handleClickOutside, false);
}

handleClickOutside(event) {
    // Получаем элемент, на который произведен клик
    const domNode = ReactDOM.findDOMNode(this);

    // Проверяем, что элемент присутствует в переменной,
    // а так же, является ли "domNode" узел потомком "event.target" узла.
    // Если не является, то скрываем элемент.
    if ((!domNode || !domNode.contains(event.target))) {
        this.setState({
            visible : false
        });
    }
}