WhoIsDT
WhoIsDT

Reputation: 705

How to make ref to component and listen for click outside?

So i'm trying to make dropdown option component. I want to show it by clicking on the button, and hide it if click was detected outside of this component.

Here is a code. I made ref, but i cant understand how to check if click was on component or not:

import React, { Component } from 'react'
import './OptionsMenu.sass'

import DropdownBox from '../DropdownBox/DropdownBox'
import Icon from '../Icon/Icon'

class OptionsMenu extends Component {
    constructor(){
        super()
        this.dropdownBoxRef = React.createRef()
    }

  handleClickOutside = event => {
    if (this.dropdownBoxRef && !this.dropdownBoxRef.contains(event.target)) {
      this.props.close()
    }
  }

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

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

  render() {
    const options = this.props.options.map(option => (
      <li className='OptionsList-Element'>
        <div className='OptionsList-ElementIcon'>
          <Icon name={option.icon} />
        </div>
        <span>{option.label}</span>
      </li>
    ))
    return (
      <DropdownBox ref={this.dropdownBoxRef} styles={this.props.styles}>
        <ul className='OptionsList'>{options}</ul>
      </DropdownBox>
    )
  }
}

export default OptionsMenu

With this code i have error: OptionsMenu.js:14 Uncaught TypeError: _this.dropdownBoxRef.contains is not a function

I know the error, cause it has not .contains(). So what to i must try to use?

Upvotes: 1

Views: 1507

Answers (2)

Piyush Zalani
Piyush Zalani

Reputation: 3934

Please find the updated code that helps to solve your issue:

import ReactDOM from "react-dom";

import "./styles.css";
import React, { Component } from "react";

class OptionsMenu extends Component {
  constructor() {
    super();
    this.dropdownBoxRef = React.createRef();
  }

  handleClickOutside = event => {
    if (this.dropdownBoxRef && !this.dropdownBoxRef.current.contains(event.target)) {
      this.props.close();
    }
  };

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

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

  render() {
    const options = ["a", "b", "c"].map(option => (
      <li className="OptionsList-Element">
        <div className="OptionsList-ElementIcon">Icon</div>
        <span>{option.label}</span>
      </li>
    ));
    return (
      <div ref={this.dropdownBoxRef} styles={this.props.styles}>
        <ul className="OptionsList">{options}</ul>
      </div>
    );
  }
}

export default OptionsMenu;

const rootElement = document.getElementById("root");
ReactDOM.render(<OptionsMenu />, rootElement);

Note: this.dropdownBoxRef.current.contains(event.target) line is the game changer.

Upvotes: 1

ApplePearPerson
ApplePearPerson

Reputation: 4439

Refs have things stored inside a property called current. So in order to actually reach your element you'll have to do this.dropdownBoxRef.current and call .contains on that.

Upvotes: 0

Related Questions