6

I have a dropdown as is shown in the following image: enter image description here

When I click the folder icon it opens and closes because showingProjectSelector property in the state that is set to false.

  constructor (props) {
    super(props)
    const { organization, owner, ownerAvatar } = props
    this.state = {
      owner,
      ownerAvatar,
      showingProjectSelector: false
    }
  }

When I click the icon, it opens and closes properly.

<i
  onClick={() => this.setState({ showingProjectSelector: !this.state.showingProjectSelector })}
  className='fa fa-folder-open'>
</i>

But what I'm trying to do is to close the dropdown when I click outside it. How can I do this without using any library?

This is the entire component: https://jsbin.com/cunakejufa/edit?js,output

Lizz Parody
  • 1,705
  • 11
  • 29
  • 48

5 Answers5

6

You could try leveraging onBlur:

<i onClick={...} onBlur={() => this.setState({showingProjectSelector: false})}/>
Arman Charan
  • 5,669
  • 2
  • 22
  • 32
1

I faced same issue with you. Solved after reading this: Detect click outside React component Please try:

Zhong Ri
  • 2,556
  • 1
  • 19
  • 23
0

You should use a High Order Component to wrap the component that you would like to listen for clicks outside it.

This component example has only one prop: "onClickedOutside" that receives a function.

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

If you want to use a tiny component (466 Byte gzipped) that already exists for this functionality then you can check out this library react-outclick.

The good thing about the library is that it also lets you detect clicks outside of a component and inside of another. It also supports detecting other types of events.

Using the library you can have something like this inside your component.

import OnOutsiceClick from 'react-outclick';

class MyComp extends Component { 

  render() {
    return (
      <OnOutsiceClick
        onOutsideClick={() => this.setState({showingProjectSelector: false})}>
        <Dropdown />
      </OnOutsiceClick>
    );
  }
}
Tushar Sharma
  • 192
  • 1
  • 15
0

Wrapper component - i.e. the one that wrapps all other components

create onClick event that runs a function handleClick.

handleClick function checks ID of the clicked event.

When ID matches it does something, otherwise it does something else.

const handleClick = (e) => {

    if(e.target.id === 'selectTypeDropDown'){
        setShowDropDown(true)
    } else {
        setShowDropDown(false); 
    }


}

So I have a dropdown menu that appears ONLY when you click on the dropdown menu, otherwise it hides it.

fruitloaf
  • 1,628
  • 15
  • 10