EN
React - detect click outside component hook (works with nested elements)
10 points
In this short article, we would like to show how in React write a custom hook that handles clicks outside component.

Note: presented in this article solution is able to handle outside clicks even clicked component contains nested elements.
Quick solution:
xxxxxxxxxx
1
const useOutideClickHandler = onOutsideClick => {
2
const ref = React.useRef(null);
3
React.useEffect(() => {
4
const handleWindowClick = e => {
5
if (onOutsideClick) {
6
let node = e.target;
7
while (node) {
8
if (ref.current === node) {
9
return;
10
}
11
node = node.parentNode;
12
}
13
onOutsideClick();
14
}
15
};
16
window.addEventListener('click', handleWindowClick);
17
return () => {
18
window.removeEventListener('click', handleWindowClick);
19
};
20
}, [onOutsideClick]);
21
return ref;
22
};
23
24
// Usage example:
25
26
const elementRef = useOutideClickHandler(() => console.log('Outside clicked!'));
27
28
<div>
29
<div ref={elementRef}>Content here ...</div>;
30
Outside area ...
31
</div>
To avoid unnecessary function recreation useOutideClickHandler()
body was moved to addOutideClickListener()
function.
xxxxxxxxxx
1
// import React from 'react';
2
// import ReactDOM from 'react-dom';
3
4
const addOutideClickListener = (ref, onOutsideClick) => {
5
const handleWindowClick = e => {
6
if (onOutsideClick) {
7
let node = e.target;
8
while (node) {
9
if (ref.current === node) {
10
return;
11
}
12
node = node.parentNode;
13
}
14
onOutsideClick();
15
}
16
};
17
window.addEventListener('click', handleWindowClick);
18
return (): void => {
19
window.removeEventListener('click', handleWindowClick);
20
};
21
};
22
23
const useOutideClickHandler = onOutsideClick => {
24
const ref = React.useRef(null);
25
React.useEffect(() => addOutideClickListener(ref, onOutsideClick), [onOutsideClick]);
26
return ref;
27
};
28
29
// Usage example:
30
31
const App = () => {
32
const buttonRef = useOutideClickHandler(() => console.log('Outside button cliked!'));
33
const divRef = useOutideClickHandler(() => console.log('Outside div cliked!'));
34
return (
35
<div>
36
<button ref={buttonRef}>Click me!</button>
37
<br /><br />
38
<div ref={divRef} style={{padding: '10px', background: '#02d456'}}>
39
Click me!
40
<div style={{padding: '10px', background: '#feeba4'}}>
41
Click me too! - it works when elements are nested too!
42
</div>
43
</div>
44
</div>
45
);
46
};
47
48
const root = document.querySelector('#root');
49
ReactDOM.render(<App />, root);