it-swarm.com.ru

Обнаружение щелчка за пределами React компонента

Я ищу способ определить, произошло ли событие щелчка за пределами компонента, как описано в этом статья . jQuery closest () используется, чтобы увидеть, имеет ли цель из события click элемент dom в качестве одного из его родителей. Если есть совпадение, событие click принадлежит одному из дочерних элементов и, таким образом, не считается находящимся вне компонента.

Поэтому в моем компоненте я хочу прикрепить обработчик кликов к окну. Когда обработчик срабатывает, мне нужно сравнить цель с дочерними элементами dom моего компонента.

Событие click содержит такие свойства, как "путь", который, кажется, содержит путь, по которому прошло событие. Я не уверен, что сравнивать или как лучше обойти это, и я думаю, что кто-то, должно быть, уже поместил это в умную функцию полезности ... Нет?

251
Thijs Koerselman

Следующее решение использует ES6 и следует передовым методам для привязки, а также для установки ссылки с помощью метода.

Чтобы увидеть это в действии: Код Sandbox demo

import React, { Component } from 'react';
import PropTypes from 'prop-types';

/**
 * Component that alerts if you click outside of it
 */
export default class OutsideAlerter extends Component {
  constructor(props) {
    super(props);

    this.setWrapperRef = this.setWrapperRef.bind(this);
    this.handleClickOutside = this.handleClickOutside.bind(this);
  }

  componentDidMount() {
    document.addEventListener('mousedown', this.handleClickOutside);
  }

  componentWillUnmount() {
    document.removeEventListener('mousedown', this.handleClickOutside);
  }

  /**
   * Set the wrapper ref
   */
  setWrapperRef(node) {
    this.wrapperRef = node;
  }

  /**
   * Alert if clicked on outside of element
   */
  handleClickOutside(event) {
    if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
      alert('You clicked outside of me!');
    }
  }

  render() {
    return <div ref={this.setWrapperRef}>{this.props.children}</div>;
  }
}

OutsideAlerter.propTypes = {
  children: PropTypes.element.isRequired,
};
341
Ben Bud

Вот решение, которое лучше всего сработало для меня без привязки событий к контейнеру:

Некоторые элементы HTML могут иметь то, что известно как « focus », например, элементы ввода. Эти элементы также будут реагировать на событие blur, когда они теряют фокус. 

Чтобы дать любому элементу возможность фокусироваться, просто убедитесь, что его атрибут tabindex имеет значение, отличное от -1. В обычном HTML это можно сделать, установив атрибут tabindex, но в React вы должны использовать tabIndex (обратите внимание на заглавную I).

Вы также можете сделать это через JavaScript с помощью element.setAttribute('tabindex',0)

Это то, для чего я его использовал, чтобы создать собственное меню DropDown.

var DropDownMenu = React.createClass({
    getInitialState: function(){
        return {
            expanded: false
        }
    },
    expand: function(){
        this.setState({expanded: true});
    },
    collapse: function(){
        this.setState({expanded: false});
    },
    render: function(){
        if(this.state.expanded){
            var dropdown = ...; //the dropdown content
        } else {
            var dropdown = undefined;
        }

        return (
            <div className="dropDownMenu" tabIndex="0" onBlur={ this.collapse } >
                <div className="currentValue" onClick={this.expand}>
                    {this.props.displayValue}
                </div>
                {dropdown}
            </div>
        );
    }
});
117
Pablo Barría Urenda

Попробовав здесь много методов, я решил использовать github.com/Pomax/react-onclickoutside из-за того, насколько он завершен.

Я установил модуль через npm и импортировал его в свой компонент:

import onClickOutside from 'react-onclickoutside'

Затем в своем классе компонентов я определил метод handleClickOutside:

handleClickOutside = () => {
  console.log('onClickOutside() method called')
}

И при экспорте моего компонента я обернул его в onClickOutside():

export default onClickOutside(NameOfComponent)

Вот и все.

79
Beau Smith

Я застрял на том же вопросе. Я немного опоздал на вечеринку, но для меня это действительно хорошее решение. Надеюсь, это поможет кому-то еще. Вам необходимо импортировать findDOMNode из react-dom

import ReactDOM from 'react-dom';
// ... ✂

componentDidMount() {
    document.addEventListener('click', this.handleClickOutside, true);
}

componentWillUnmount() {
    document.removeEventListener('click', this.handleClickOutside, true);
}

handleClickOutside = event => {
    const domNode = ReactDOM.findDOMNode(this);

    if (!domNode || !domNode.contains(event.target)) {
        this.setState({
            visible: false
        });
    }
}

Подход Реакции Крюков (16.8 +)

Вы можете создать многоразовый хук с именем useComponentVisible.

import { useState, useEffect, useRef } from 'react';

export default function useComponentVisible(initialIsVisible) {
    const [isComponentVisible, setIsComponentVisible] = useState(initialIsVisible);
    const ref = useRef(null);

    const handleClickOutside = (event) => {
        if (ref.current && !ref.current.contains(event.target)) {
            setIsComponentVisible(false);
        }
    };

    useEffect(() => {
        document.addEventListener('click', handleClickOutside, true);
        return () => {
            document.removeEventListener('click', handleClickOutside, true);
        };
    });

    return { ref, isComponentVisible, setIsComponentVisible };
}

Затем в компонент вы хотите добавить функциональность, чтобы сделать следующее:

const DropDown = () => {
    const { ref, isComponentVisible } = useComponentVisible(true);
    return (
       <div ref={ref}>
          {isComponentVisible && (<p>Dropdown Component</p>)}
       </div>
    );

}

Найти коды и ящик пример здесь.

46
Paul Fitzgerald

Я нашел решение благодаря Бену Альперту на обсуждения.reactjs.org . Предложенный подход прикрепляет обработчик к документу, но это оказалось проблематичным. Нажатие на один из компонентов в моем дереве привело к повторному отображению, при котором элемент обновления, по которому щелкнули, удалялся при обновлении. Поскольку повторное рендеринг из React происходит до вызова обработчика тела документа, элемент не был обнаружен как «внутри» дерева.

Решением этой проблемы было добавление обработчика в корневой элемент приложения. 

главный:

window.__myapp_container = document.getElementById('app')
React.render(<App/>, window.__myapp_container)

составная часть:

import { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';

export default class ClickListener extends Component {

  static propTypes = {
    children: PropTypes.node.isRequired,
    onClickOutside: PropTypes.func.isRequired
  }

  componentDidMount () {
    window.__myapp_container.addEventListener('click', this.handleDocumentClick)
  }

  componentWillUnmount () {
    window.__myapp_container.removeEventListener('click', this.handleDocumentClick)
  }

  /* using fat arrow to bind to instance */
  handleDocumentClick = (evt) => {
    const area = ReactDOM.findDOMNode(this.refs.area);

    if (!area.contains(evt.target)) {
      this.props.onClickOutside(evt)
    }
  }

  render () {
    return (
      <div ref='area'>
       {this.props.children}
      </div>
    )
  }
}
37
Thijs Koerselman

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

Вот подход, который работал для меня:

// Inside the component:
onBlur(event) {
    // currentTarget refers to this component.
    // relatedTarget refers to the element where the user clicked (or focused) which
    // triggered this event.
    // So in effect, this condition checks if the user clicked outside the component.
    if (!event.currentTarget.contains(event.relatedTarget)) {
        // do your thing.
    }
},

Надеюсь это поможет.

22
Niyaz

[Обновить] Решение с помощью Реагировать ^ 16.8 с использованием Хуки

CodeSandbox

import React, { useEffect, useRef, useState } from 'react';

const SampleComponent = () => {
    const [clickedOutside, setClickedOutside] = useState(false);
    const myRef = useRef();

    const handleClickOutside = e => {
        if (!myRef.current.contains(e.target)) {
            setClickedOutside(true);
        }
    };

    const handleClickInside = () => setClickedOutside(false);

    useEffect(() => {
        document.addEventListener('mousedown', handleClickOutside);
        return () => document.removeEventListener('mousedown', handleClickOutside);
    });

    return (
        <button ref={myRef} onClick={handleClickInside}>
            {clickedOutside ? 'Bye!' : 'Hello!'}
        </button>
    );
};

export default SampleComponent;

Решение с помощью React ^ 16.3:

CodeSandbox

import React, { Component } from "react";

class SampleComponent extends Component {
  state = {
    clickedOutside: false
  };

  componentDidMount() {
    document.addEventListener("mousedown", this.handleClickOutside);
  }

  componentWillUnmount() {
    document.removeEventListener("mousedown", this.handleClickOutside);
  }

  myRef = React.createRef();

  handleClickOutside = e => {
    if (!this.myRef.current.contains(e.target)) {
      this.setState({ clickedOutside: true });
    }
  };

  handleClickInside = () => this.setState({ clickedOutside: false });

  render() {
    return (
      <button ref={this.myRef} onClick={this.handleClickInside}>
        {this.state.clickedOutside ? "Bye!" : "Hello!"}
      </button>
    );
  }
}

export default SampleComponent;
11
onoya

Вот мой подход (демо - https://jsfiddle.net/agymay93/4/ ):

Я создал специальный компонент с именем WatchClickOutside, и его можно использовать следующим образом (я предполагаю синтаксис JSX):

<WatchClickOutside onClickOutside={this.handleClose}>
  <SomeDropdownEtc>
</WatchClickOutside>

Вот код компонента WatchClickOutside:

import React, { Component } from 'react';

export default class WatchClickOutside extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  componentWillMount() {
    document.body.addEventListener('click', this.handleClick);
  }

  componentWillUnmount() {
    // remember to remove all events to avoid memory leaks
    document.body.removeEventListener('click', this.handleClick);
  }

  handleClick(event) {
    const {container} = this.refs; // get container that we'll wait to be clicked outside
    const {onClickOutside} = this.props; // get click outside callback
    const {target} = event; // get direct click event target

    // if there is no proper callback - no point of checking
    if (typeof onClickOutside !== 'function') {
      return;
    }

    // if target is container - container was not clicked outside
    // if container contains clicked target - click was not outside of it
    if (target !== container && !container.contains(target)) {
      onClickOutside(event); // clicked outside - fire callback
    }
  }

  render() {
    return (
      <div ref="container">
        {this.props.children}
      </div>
    );
  }
}
4
pie6k

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

<div style={{
        position: 'fixed',
        top: '0', right: '0', bottom: '0', left: '0',
        zIndex: '1000',
      }} onClick={() => handleOutsideClick()} >
    <Content style={{position: 'absolute'}}/>
</div>

Как и сейчас, если вы добавите обработчик кликов по контенту, событие также будет распространено на верхний элемент div и, следовательно, вызовет handlerOutsideClick. Если это не ваше желаемое поведение, просто остановите передачу событий в вашем обработчике.

<Content style={{position: 'absolute'}} onClick={e => {
                                          e.stopPropagation();
                                          desiredFunctionCall();
                                        }}/>

`

2
Anthony Garant

Чтобы расширить принятый ответ, сделанный Ben Bud , если вы используете styled-компоненты, передача refs таким образом выдаст вам ошибку, такую ​​как "this.wrapperRef.contains не является функцией".

Предлагаемое исправление в комментариях, чтобы обернуть стилизованный компонент с помощью div и передать туда ссылку, работает . Сказав это, в своих документах они уже объясняют причину этого и правильное использование ссылки внутри styled-компонентов:

Передача ref prop в стилизованный компонент даст вам экземпляр оболочки StyledComponent, но не в базовый узел DOM. Это связано с тем, как работают рефери. Невозможно напрямую вызывать методы DOM, например focus, для наших оболочек . Чтобы получить ссылку на фактический, обернутый DOM-узел, вместо этого передайте обратный вызов в службу innerRef.

Вот так:

<StyledDiv innerRef={el => { this.el = el }} />

Затем вы можете получить к нему доступ непосредственно в функции handleClickOutside:

handleClickOutside = e => {
    if (this.el && !this.el.contains(e.target)) {
        console.log('clicked outside')
    }
}

Это также относится к подходу "onBlur":

componentDidMount(){
    this.el.focus()
}
blurHandler = () => {
    console.log('clicked outside')
}
render(){
    return(
        <StyledDiv
            onBlur={this.blurHandler}
            tabIndex="0"
            innerRef={el => { this.el = el }}
        />
    )
}
2
Eric Delli Compagni

Я использовал этот модуль (у меня нет связи с автором)

npm install react-onclickout --save

const ClickOutHandler = require('react-onclickout');
 
class ExampleComponent extends React.Component {
 
  onClickOut(e) {
    if (hasClass(e.target, 'ignore-me')) return;
    alert('user clicked outside of the component!');
  }
 
  render() {
    return (
      <ClickOutHandler onClickOut={this.onClickOut}>
        <div>Click outside of me!</div>
      </ClickOutHandler>
    );
  }
}

Это хорошо сработало.

2
ErichBSchulz

Решение с React Hooks (16,8+)

tl; dr ( Codesandbox ):

"Внутренний" компонент

function OutsideClickComp() {
  const innerRef = useRef(null);
  useOuterClickNotifier(
    // if you want to call it once only,
    // don't provide an anonymous function here
    e => alert("clicked outside of this component!"),
    innerRef
  );
  return (
    <div ref={innerRef}>
      inside component
    </div>
  );
}

Таможенный Крюк

function useOuterClickNotifier(onOuterClick, innerRef) {
  useLayoutEffect(
    () => {
      document.addEventListener("click", handleClick);
      // called for each previous layout effect
      return () => document.removeEventListener("click", handleClick);

      function handleClick(e) {
        !innerRef.current.contains(e.target) && onOuterClick(e);
      }
    },
    [onOuterClick, innerRef] // invoke again, if inputs have changed
  );
}

Вот мое решение: мне пришлось реализовать какое-то всплывающее меню, которое закрывается автоматически при нажатии вне контейнера меню. Используя хуки, IMO дает следующие преимущества:

  • побочный эффект/логика регистрации внешних кликов может быть полностью удалена и упростит «внутренний» компонент
  • повторное использование useOuterClickNotifier везде
  • нет дополнительного компонента оболочки (заменяется логикой хука)

Затем, в зависимости от вашего варианта использования, вы можете сделать что-нибудь во внешнем обратном вызове клика, который здесь для простоты обозначен как alert("clicked outside of this component!"). Например. установите какое-либо состояние с помощью useState hook или вызовите обратный вызов (в моем случае), чтобы открыть/закрыть всплывающее меню.

Правка: useLayoutEffect против useEffect

useEffect запускается после фазы рендеринга во время события deferred, тогда как useLayoutEffect вызывается синхронно после каждого рендеринга. Таким образом, useLayoutEffect ведет себя как методы жизненного цикла componentDidMount и componentDidUpdate в компонентах класса. Он используется для проверки того, что при изменении входных данных функции подключения useOuterClickNotifier прослушиватель щелчков предыдущего документа не может быть запущен до того, как он будет незарегистрирован. Смотрите время ловушки эффекта и useLayoutEffect

Надеюсь, это поможет.

2
ford04
componentWillMount(){

  document.addEventListener('mousedown', this.handleClickOutside)
}

handleClickOutside(event) {

  if(event.path[0].id !== 'your-button'){
     this.setState({showWhatever: false})
  }
}

Событие path[0] - последний нажатый элемент

2
Iván

На это уже есть много ответов, но они не относятся к e.stopPropagation() и не позволяют нажимать на ссылки реакции вне элемента, который вы хотите закрыть.

Из-за того, что React имеет свой собственный искусственный обработчик событий, вы не можете использовать документ в качестве базы для слушателей событий. Перед этим вам нужно e.stopPropagation(), так как React использует сам документ. Если вы используете, например, document.querySelector('body') вместо. Вы можете предотвратить переход по ссылке Реакт. Ниже приведен пример того, как я реализую щелчок снаружи и близко.
Использует ES6 и React 16.3 .

import React, { Component } from 'react';

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isOpen: false,
    };

    this.insideContainer = React.createRef();
  }

  componentWillMount() {
    document.querySelector('body').addEventListener("click", this.handleClick, false);
  }

  componentWillUnmount() {
    document.querySelector('body').removeEventListener("click", this.handleClick, false);
  }

  handleClick(e) {
    /* Check that we've clicked outside of the container and that it is open */
    if (!this.insideContainer.current.contains(e.target) && this.state.isOpen === true) {
      e.preventDefault();
      e.stopPropagation();
      this.setState({
        isOpen: false,
      })
    }
  };

  togggleOpenHandler(e) {
    e.preventDefault();

    this.setState({
      isOpen: !this.state.isOpen,
    })
  }

  render(){
    return(
      <div>
        <span ref={this.insideContainer}>
          <a href="#open-container" onClick={(e) => this.togggleOpenHandler(e)}>Open me</a>
        </span>
        <a href="/" onClick({/* clickHandler */})>
          Will not trigger a click when inside is open.
        </a>
      </div>
    );
  }
}

export default App;
1
Richard Herries

Я сделал это частично, следуя это и следуя официальным документам React по обработке ссылок, которые требуют реакции ^ 16.3. Это единственное, что сработало для меня, попробовав некоторые другие предложения здесь ...

class App extends Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }
  componentWillMount() {
    document.addEventListener("mousedown", this.handleClick, false);
  }
  componentWillUnmount() {
    document.removeEventListener("mousedown", this.handleClick, false);
  }
  handleClick = e => {
    if (this.inputRef.current === e.target) {
      return;
    }
    this.handleclickOutside();
  };
handleClickOutside(){
...***code to handle what to do when clicked outside***...
}
render(){
return(
<div>
...***code for what's outside***...
<span ref={this.inputRef}>
...***code for what's "inside"***...
</span>
...***code for what's outside***
)}}
1
RawCode

Пример со Стратегией

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

Поскольку это скорее поведение, я подумал о стратегии и придумал следующее. 

Я новичок в React, и мне нужно немного помочь, чтобы сохранить шаблон в случаях использования 

Пожалуйста, просмотрите и скажите мне, что вы думаете.

ClickOutsideBehavior

import ReactDOM from 'react-dom';

export default class ClickOutsideBehavior {

  constructor({component, appContainer, onClickOutside}) {

    // Can I extend the passed component's lifecycle events from here?
    this.component = component;
    this.appContainer = appContainer;
    this.onClickOutside = onClickOutside;
  }

  enable() {

    this.appContainer.addEventListener('click', this.handleDocumentClick);
  }

  disable() {

    this.appContainer.removeEventListener('click', this.handleDocumentClick);
  }

  handleDocumentClick = (event) => {

    const area = ReactDOM.findDOMNode(this.component);

    if (!area.contains(event.target)) {
        this.onClickOutside(event)
    }
  }
}

Образец использования

import React, {Component} from 'react';
import {APP_CONTAINER} from '../const';
import ClickOutsideBehavior from '../ClickOutsideBehavior';

export default class AddCardControl extends Component {

  constructor() {
    super();

    this.state = {
      toggledOn: false,
      text: ''
    };

    this.clickOutsideStrategy = new ClickOutsideBehavior({
      component: this,
      appContainer: APP_CONTAINER,
      onClickOutside: () => this.toggleState(false)
    });
  }

  componentDidMount () {

    this.setState({toggledOn: !!this.props.toggledOn});
    this.clickOutsideStrategy.enable();
  }

  componentWillUnmount () {
    this.clickOutsideStrategy.disable();
  }

  toggleState(isOn) {

    this.setState({toggledOn: isOn});
  }

  render() {...}
}

Заметки

Я подумал о сохранении переданных хуков жизненного цикла component и переопределил их с помощью методов, аналогичных следующим:

const baseDidMount = component.componentDidMount;

component.componentDidMount = () => {
  this.enable();
  baseDidMount.call(component)
}

component - это компонент, переданный в конструктор ClickOutsideBehavior.
Это удалит включить/отключить шаблон от пользователя этого поведения, но это выглядит не очень хорошо, хотя

1
kidroca

Моя самая большая проблема со всеми остальными ответами - это необходимость фильтровать события кликов от корня/родителя вниз. Я обнаружил, что проще всего было просто установить элемент-брат с позицией: fixed, z-index 1 за выпадающим списком и обработать событие click для фиксированного элемента внутри того же компонента. Сохраняет все централизованным для данного компонента.

Пример кода

#HTML
<div className="parent">
  <div className={`dropdown ${this.state.open ? open : ''}`}>
    ...content
  </div>
  <div className="outer-handler" onClick={() => this.setState({open: false})}>
  </div>
</div>

#SASS
.dropdown {
  display: none;
  position: absolute;
  top: 0px;
  left: 0px;
  z-index: 100;
  &.open {
    display: block;
  }
}
.outer-handler {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    opacity: 0;
    z-index: 99;
    display: none;
    &.open {
      display: block;
    }
}
1
metroninja

Чтобы 'focus' Solution работал для выпадающего списка с прослушивателями событий, вы можете добавить их с помощью onMouseDown event вместо onClick . Таким образом, событие сработает, и после этого всплывающее окно закроется так:

<TogglePopupButton
                    onClick = { this.toggleDropup }
                    tabIndex = '0'
                    onBlur = { this.closeDropup }
                />
                { this.state.isOpenedDropup &&
                <ul className = { dropupList }>
                    { this.props.listItems.map((item, i) => (
                        <li
                            key = { i }
                            onMouseDown = { item.eventHandler }
                        >
                            { item.itemName}
                        </li>
                    ))}
                </ul>
                }
0
idtmc

Я нашел это из статьи ниже:

render () { return ( {this.node = node;}} > Переключить Popover {this.state.popupVisible && ( Я трещать! )} ); } }

Вот отличная статья об этой проблеме: "Обрабатывать клики вне компонентов React"https://larsgraubner.com/handle-outside-clicks-react/

0
Amanda Koster

Я сделал решение на все случаи жизни.

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

В этом примере компонента есть только одна опора: "onClickedOutside", которая получает функцию.

ClickedOutside.js
import React, { Component } from "react";

export default class ClickedOutside extends Component {
  componentDidMount() {
    document.addEventListener("mousedown", this.handleClickOutside);
  }

  componentWillUnmount() {
    document.removeEventListener("mousedown", this.handleClickOutside);
  }

  handleClickOutside = event => {
    // IF exists the Ref of the wrapped component AND his dom children doesnt have the clicked component 
    if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
      // A props callback for the ClikedClickedOutside
      this.props.onClickedOutside();
    }
  };

  render() {
    // In this piece of code I'm trying to get to the first not functional component
    // Because it wouldn't work if use a functional component (like <Fade/> from react-reveal)
    let firstNotFunctionalComponent = this.props.children;
    while (typeof firstNotFunctionalComponent.type === "function") {
      firstNotFunctionalComponent = firstNotFunctionalComponent.props.children;
    }

    // Here I'm cloning the element because I have to pass a new prop, the "reference" 
    const children = React.cloneElement(firstNotFunctionalComponent, {
      ref: node => {
        this.wrapperRef = node;
      },
      // Keeping all the old props with the new element
      ...firstNotFunctionalComponent.props
    });

    return <React.Fragment>{children}</React.Fragment>;
  }
}
0
Higor Lorenzon

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

В этом случае мы вызываем this.closeDropdown() всякий раз, когда значение clickCount изменяется.

Метод incrementClickCount запускается в контейнере .app, но не в .dropdown, потому что мы используем event.stopPropagation() для предотвращения всплытия событий.

Ваш код может выглядеть примерно так:

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            clickCount: 0
        };
    }
    incrementClickCount = () => {
        this.setState({
            clickCount: this.state.clickCount + 1
        });
    }
    render() {
        return (
            <div className="app" onClick={this.incrementClickCount}>
                <Dropdown clickCount={this.state.clickCount}/>
            </div>
        );
    }
}
class Dropdown extends Component {
    constructor(props) {
        super(props);
        this.state = {
            open: false
        };
    }
    componentDidUpdate(prevProps) {
        if (this.props.clickCount !== prevProps.clickCount) {
            this.closeDropdown();
        }
    }
    toggleDropdown = event => {
        event.stopPropagation();
        return (this.state.open) ? this.closeDropdown() : this.openDropdown();
    }
    render() {
        return (
            <div className="dropdown" onClick={this.toggleDropdown}>
                ...
            </div>
        );
    }
}
0
wdm
import ReactDOM from 'react-dom' ;

class SomeComponent {

  constructor(props) {
    // First, add this to your constructor
    this.handleClickOutside = this.handleClickOutside.bind(this);
  }

  componentWillMount() {
    document.addEventListener('mousedown', this.handleClickOutside, false); 
  }

  // Unbind event on unmount to prevent leaks
  componentWillUnmount() {
    window.removeEventListener('mousedown', this.handleClickOutside, false);
  }

  handleClickOutside(event) {
    if(!ReactDOM.findDOMNode(this).contains(event.path[0])){
       console.log("OUTSIDE");
    }
  }
}
0
dipole_moment