Detect Click Outside React Component

Detect click outside React component

The following solution uses ES6 and follows best practices for binding as well as setting the ref through a method.

To see it in action:

  • Hooks Implementation
  • Class Implementation After React 16.3
  • Class Implementation Before React 16.3

Hooks Implementation:

import React, { useRef, useEffect } from "react";

/**
* Hook that alerts clicks outside of the passed ref
*/
function useOutsideAlerter(ref) {
useEffect(() => {
/**
* Alert if clicked on outside of element
*/
function handleClickOutside(event) {
if (ref.current && !ref.current.contains(event.target)) {
alert("You clicked outside of me!");
}
}
// Bind the event listener
document.addEventListener("mousedown", handleClickOutside);
return () => {
// Unbind the event listener on clean up
document.removeEventListener("mousedown", handleClickOutside);
};
}, [ref]);
}

/**
* Component that alerts if you click outside of it
*/
export default function OutsideAlerter(props) {
const wrapperRef = useRef(null);
useOutsideAlerter(wrapperRef);

return <div ref={wrapperRef}>{props.children}</div>;
}

Class Implementation:

After 16.3

import React, { Component } from "react";

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

this.wrapperRef = React.createRef();
this.handleClickOutside = this.handleClickOutside.bind(this);
}

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

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

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

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

Before 16.3

import React, { Component } from "react";

/**
* 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>;
}
}

Reactjs hide div when click outside or click men

You can use useRef hook and bind event listener to your div, then change your isOpen state.

See Detect click outside React component

React detect click outside component clearing my state

When you call useOutsideAlerter and pass onClickOutside handler it captures tableFilterState value and use it in a subsequent calls. This is a stale state. You could try this approach or use refs as described in docs:

const onClickOutside = () => {
setTableFilterState(tableFilterState => ({
...tableFilterState,
state: INACTIVE,
}));
}

Detect click outside React component using hooks

This is possible.

You can create a reusable hook called useComponentVisible

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

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

const handleHideDropdown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
setIsComponentVisible(false);
}
};

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

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

return { ref, isComponentVisible, setIsComponentVisible };
}

Then in the component you wish to add the functionality to do the following:

const DropDown = () => {

const { ref, isComponentVisible } = useComponentVisible(true);

return (
<div ref={ref}>
{isComponentVisible && (<p>Going into Hiding</p>)}
</div>
);

}

Find a codesandbox example here.

Detect click outside component react hooks

the useRef API should be used like this:

import React, { useState, useRef, useEffect } from "react";
import ReactDOM from "react-dom";

function App() {
const wrapperRef = useRef(null);
const [isVisible, setIsVisible] = useState(true);

// below is the same as componentDidMount and componentDidUnmount
useEffect(() => {
document.addEventListener("click", handleClickOutside, false);
return () => {
document.removeEventListener("click", handleClickOutside, false);
};
}, []);

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

return (
isVisible && (
<div className="menu" ref={wrapperRef}>
<p>Menu</p>
</div>
)
);
}

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

React closing a dropdown when click outside

You need to handle the ref passed so that it actually attaches to something:

const Home = forwardRef(({ open, setOpen }, ref) => {
console.log(open);

return (
<section ref={ref}>
<h1>Feels good to be home!</h1>

<button className="secondary" onClick={() => setOpen(!open)}>
Dropdown Toggle
</button>

{open && (
<ul>
<li>One</li>
<li>Two</li>
</ul>
)}
</section>
);
});

Edit React withClickOutside (forked)

React detect click outside a component with custom hook

I think it was just a typo. in your sandbox i fixed the handleClickOutside like this:

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

there was missing a "t" in the "target" in !ref.current.contains(event.target)

Is it working for you now?



Related Topics



Leave a reply



Submit