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>
);
});
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
How to Show a Running Progress Bar While Page Is Loading
Ellipsis in the Middle of a Text (MAC Style)
How to Preserve Aspect Ratio When Scaling Image Using One (Css) Dimension in IE6
Performance Impact of Using CSS/JavaScript Source-Maps in Production
Dynamically Loading CSS File Using JavaScript with Callback Without Jquery
Load Less.Js Rules Dynamically
Prevent Address-Bar Hiding in Mobile Browsers
Modify a CSS Rule Object with JavaScript
Get Computed Font-Family in JavaScript
Read :Hover Pseudo Class with JavaScript
Html5 Video - Percentage Loaded
How to Use JavaScript Conditionally Like CSS3 Media Queries, Orientation
Combination of Animation and Transition Not Working Properly
Why Is Element Not Being Shown Before Alert
How to Calculate Date Difference in JavaScript
JavaScript in Uiwebview Callback to C/Objective-C
Convert String to Variable Name in JavaScript
Clientheight/Clientwidth Returning Different Values on Different Browsers