Handling "clicks outside" in ReactJS

author's avatar
Kees de Kooter
Sep 5 2019 15:52 • 1 min read

A very common component in (web-)applications is a popup/modal/dialog. You want to interact with the user without navigating away from the current view. How to create such a component will be the subject of another blogpost or two - as no framework to date supports this out of the box.

After the modal/dialog/whatever is popped up the expectation is that when clicked/tapped outside the thing closes again.

In order to make this work in ReactJS we have to hook into the actual DOM like so:

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

export default class ClickOutsideWrapper extends Component {

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

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

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

  handleClickOutside = (event) => {
    if (this.container && !this.container.contains(event.target)) {
      event.preventDefault()
      event.stopPropagation()
      this.props.onClickOutside()
    }
  }

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

To use this component you wrap it around the thing popping up:

<ClickOutsideWrapper onClickOutside={this.onClickedOutside}>
    <YourPopupComponent/>
</ClickOutsideWrapper>

This code is based on this SO answer: https://stackoverflow.com/a/42234988/26496