Detect click outside a react component using React Hooks
Many more developers are facing difficulties to detect the click event outside of the element in ReactJS therefore we’ll show you how to detect click outside a react component using React Hooks.
Why do we need it?
In the real project, we might need to handle an event when we click outside of a react component. Based on the outside event we can show or hide the element or manage some other components.
Detect click outside a react component
Use the following code to detect outside click events.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | function useVisible(initialIsVisible) { const [isVisible, setIsVisible] = useState(initialIsVisible); const ref = useRef(null); const handleClickOutside = (event) => { if (ref.current && !ref.current.contains(event.target)) { setIsVisible(false); } }; useEffect(() => { document.addEventListener('click', handleClickOutside, true); return () => { document.removeEventListener('click', handleClickOutside, true); }; }, []); return { ref, isVisible, setIsVisible }; } |
In the above code, we have added the event listener of the click event and remove it on component unmount.
Also we are handling the initial state of the component for visibility and reference of the component to detect outside click.
At last we are returning the reference, visibility state and method to change the visibility state and those attributes are useful to detect outside click.
Example
Let’s take a previous example, where we have implemented color picker in react. Now we will show a color picker on click of the input field and hide it when we detect a click outside a color picker.
First, we have to create a custom hooks to detect click event. Use the below code to create a custom hooks.
useVisible.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import { useState, useRef, useEffect } from "react"; function useVisible(initialIsVisible) { const [isVisible, setIsVisible] = useState(initialIsVisible); const ref = useRef(null); const handleClickOutside = (event) => { if (ref.current && !ref.current.contains(event.target)) { setIsVisible(false); } }; useEffect(() => { document.addEventListener('click', handleClickOutside, true); return () => { document.removeEventListener('click', handleClickOutside, true); }; }, []); return { ref, isVisible, setIsVisible }; } export default useVisible; |
Use the above hooks in the App.js
file to manage show/hide color picker on click.
App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import React, { useState } from 'react'; import { SketchPicker } from 'react-color'; import useVisible from './useVisible'; function App() { const [colorHexCode, setColorHexCode] = useState('#000000'); const { ref, isVisible, setIsVisible } = useVisible(false); return ( <div className="App"> <h3>React color picker - <a href="https://www.cluemediator.com">Clue Mediator</a></h3> <div> <b>Selected Hex Color: </b> <span className="color-box" style={{ background: colorHexCode }}></span> {colorHexCode} </div> <br /> <input type="text" readOnly value={colorHexCode} onClick={e => setIsVisible(!isVisible)} /> {isVisible && <div style={{ position: 'absolute' }} ref={ref}> <SketchPicker color={colorHexCode} onChange={e => setColorHexCode(e.hex)} /> </div>} </div> ); } export default App; |
We are also showing the selected color in box along with the color code for live view.
Output
Run the project and check out the output in browser.
That’s it for today.
Thank you for reading. Happy Coding..!!
This is an excellent solution to implement the click outside event listener. Great work!
How would you modify this to hide the element on a second click in the input box? So it shows it first, then hides it on a second click.
We are using refs to manage the element visibility.
This screwed me up as well, as the example is great except for this case, where the button and ref race each other and the ref hides, then the other immediately flips the value to show again, so you can’t hide clicking the original target, which in my case was a button. This felt like a bad experience to me, but I figured out an easy solution:
in my case I had a button that toggled visibility and what I did is duplicated it, and then wrapped one in an {isVisible} and the other in an {!isVisible} and then instead of toggling like suggested “setIsVisible(!isVisible)”, switch it so the button in the {isVisible} does this: setIsVisible(isVisible), and {!isVisible} does this: setIsVisible(!isVisible).
Now you can toggle the original button to hide and still click outside. Great example code otherwise.
Hi Ivan, In our example, it’s an input field to always show the color picker on textbox’s click.