Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handle outside click closes on clicking the modal itself. Basically shouldn't close when clicked anywhere but outside of the modal

This code has worked for me before but i'm not sure what's changed in this other component i'm trying to use it in.

I've tried using hooks to open and close modal and just plain on click event listener but both times it closes on clicking anywhere on the page.

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

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

  handleOutsideClick = (e) => {
    if (this.state.showInfoModal && !this.node.contains(e.target)) this.handleInfoToggle();
    console.log(this.state.showInfoModal, e.target, this.node, 'clicked outside');
  }

  handleInfoToggle = (event) => {
    const { showInfoModal } = this.state;

    if (event) event.preventDefault();
    this.setState({ showInfoModal: !showInfoModal });
  };

renderSomething = (args) => {
 return(
   <span ref={(node) => { this.node = node; }}>
   {something === true && <span className={styles.somethingelse}> 
   <HintIcon onClick={this.handleInfoToggle} /></span>}
    <Modal visible={showInfoModal} onCancel={this.handleInfoToggle}>
     some information to show
    </Modal>
   </span>
  )
}

render() => {
  return (
   {this.renderSomething(args)}
  )
}

Not sure if this is enough info. but this is driving me nuts.

I also tried adding a dontCloseModal function that someone had suggested:

  dontCloseModal = (e) => {
    e.stopPropagation();
    console.log(e);
    this.setState({
      showInfoModal: true
    });
  }

 <div onClick={this.dontCloseModal}></div>

(((this would go around the <Modal/> component )))

  const refs = React.createRef(); // Setup to wrap one child
  const handleClick = (event) => {
    const isOutside = () => {
      return !refs.current.contains(event.target);
    };
    if (isOutside) {
      onClick();
    }
  };

  useEffect(() => {
    document.addEventListener('click', handleClick);

    return function() {
      document.removeEventListener('click', handleClick);
    };
  });

  return (element, idx) => React.cloneElement(element, { ref: refs[idx] });
}

export default ClickOutside;

Tried using a component like this ^^ and adding <ClickOutside onClick={this.closeInfoModal()}></ClickOutside> But same issue with this too- closes on click anywhere including inside modal

like image 724
trailblazer Avatar asked Jun 06 '19 18:06

trailblazer


1 Answers

After playing with this a little bit, it seems that you should also useRef here. This will allow you to control toggling the modal if the user clicks outside and inside the modal's target.

There are a lot of sophisticated ways to achieve this. However, since we are dealing with hooks here, it would be best to use a custom hook.

Introducing useOnClick 💫:

// Custom hook for controling user clicks inside & outside
function useOnClick(ref, handler) {
    useEffect(() => {
        const listener = event => {
            // Inner Click: Do nothing if clicking ref's element or descendent elements, similar to the solution I gave in my comment stackoverflow.com/a/54633645/4490712 
            if (!ref.current || ref.current.contains(event.target)) {
                return;
            }
            // Outer Click: Do nothing if clicking wrapper ref
            if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
                return;
            }

            handler(event);
        };

        // Here we are subscribing our listener to the document
        document.addEventListener("mousedown", listener);

        return () => {
            // And unsubscribing it when we are no longer showing this component
            document.removeEventListener("mousedown", listener);
        };
    }, []); // Empty array ensures that effect is only run on mount and unmount
}

Watch this Demo in CodeSandBox so you can see how this is implemented using hooks.

Welcome to StackOverflow!

like image 130
Tomer Avatar answered Oct 03 '22 19:10

Tomer