Handle Click Outside React Component.
Tags: react, webdev, tutorial, programming
Published: 2022-06-08 11:08:06

In this tutorial we are going to make a dropdown component, creating the drop down functionality, and then handling the click outside, it's a perfect use case on how to handle click outside a react comoponent, let's do that :fire:

_first create a dropdown button, for this am using a free element from the taillwind ui library kit:_

```javascript
export function DropDown() {
    return(
        <div className="relative inline-block text-left">
                <div>
                    <button type="button" className="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-indigo-500" id="menu-button" aria-expanded="true" aria-haspopup="true">
                        Options
                        <svg className="-mr-1 ml-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
                            <path fillRule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clipRule="evenodd" />
                        </svg>
                    </button>
                </div>
                <div className="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="menu-button">
                    <div className="py-1" role="none">
                        <a href="#" className="text-gray-700 block px-4 py-2 text-sm" role="menuitem" id="menu-item-0">Account settings</a>
                        <a href="#" className="text-gray-700 block px-4 py-2 text-sm" role="menuitem" id="menu-item-1">Support</a>
                        <a href="#" className="text-gray-700 block px-4 py-2 text-sm" role="menuitem" id="menu-item-2">License</a>
                        <form method="POST" action="#" role="none">
                            <button type="submit" className="text-gray-700 block w-full text-left px-4 py-2 text-sm" role="menuitem" id="menu-item-3">Sign out</button>
                        </form>
                    </div>
                </div>
            </div>
    );
}
```

![dropdown no effect](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/249yjva6qfjj4agsqfhh.png)

_till now the button has no dropdown effect, let's add some state to handle this._

```javascript
import { useState } from "react";
export function DropDown() {
     const [dropDownOpen, setDropDownOpen] = useState(false)
     const toggleDropDown = () => setDropDownOpen(!dropDownOpen)
      ...
    {/* allow the button to change state */}
     <button 
      ...
     onClick={toggleDropDown}>
      ...
    {/* show the options area based on the dropdown state */}
    { dropDownOpen &&
                <div className="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="menu-button">
                    <div className="py-1" role="none">
                        <a href="#" className="text-gray-700 block px-4 py-2 text-sm" role="menuitem" id="menu-item-0">Account settings</a>
                        <a href="#" className="text-gray-700 block px-4 py-2 text-sm" role="menuitem" id="menu-item-1">Support</a>
                        <a href="#" className="text-gray-700 block px-4 py-2 text-sm" role="menuitem" id="menu-item-2">License</a>
                        <form method="POST" action="#" role="none">
                            <button type="submit" className="text-gray-700 block w-full text-left px-4 py-2 text-sm" role="menuitem" id="menu-item-3">Sign out</button>
                        </form>
                    </div>
                </div>}
    ...
```

![drop down working](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wuvkf3l1pwsmiuiq3lfg.gif)

_Cool, the dropdown works fine now, but nothing happen when we click outside, let's use the useEffect() and useRef() react hooks  to get the desired result._

```javascript
import { useEffect, useRef, useState } from "react";
export function DropDown() {
...
const dropDownRef = useRef<HTMLDivElement>(null);

//change dropdown state to false when clicking outside.
useEffect(() => {
        function handler(event:any) {
            if(!dropDownRef.current?.contains(event.target)) {
                setDropDownOpen(false);
            }
        }
        window.addEventListener('click', handler)
        return () => window.removeEventListener('click', handler)
    }, []);
...
return(
        <div 
        ref={dropDownRef}
        ...>
...
```

![click outside working](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5eep058oiuz7jwlvhcjn.gif)

_you see how this is easy, before finishing the tutorial why not adding some transition effect to the dropdown area, for this let's use the Transition component from headles ui_.
`npm install @headlessui/react
`
_full code of the component._
```javascript
import { Transition } from "@headlessui/react";
import { useEffect, useRef, useState } from "react";

export function DropDown() {
    const [dropDownOpen, setDropDownOpen] = useState(false);
    const toggleDropDown = () => setDropDownOpen(!dropDownOpen)
    const dropDownRef = useRef<HTMLDivElement>(null);

    useEffect(() => {
        function handler(event: any) {
            if (!dropDownRef.current?.contains(event.target)) {
                setDropDownOpen(false);
            }
        }
        window.addEventListener('click', handler)
        return () => window.removeEventListener('click', handler)
    }, []);

    return (
        <div
            ref={dropDownRef}
            className="relative inline-block text-left">
            <div>
                <button
                    type="button"
                    className="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-indigo-500" id="menu-button" aria-expanded="true" aria-haspopup="true"
                    onClick={toggleDropDown}>
                    Options
                    <svg className="-mr-1 ml-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
                        <path fillRule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clipRule="evenodd" />
                    </svg>
                </button>
            </div>
            <Transition
                show={dropDownOpen}
                enter="transition ease-out duration-100"
                enterFrom="transform opacity-0 scale-95"
                enterTo="transform opacity-100 scale-100"
                leave="transform opacity-100 scale-100"
                leaveFrom="transform opacity-100 scale-100"
                leaveTo="transform opacity-0 scale-95">
                <div className="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="menu-button">
                    <div className="py-1" role="none">
                        <a href="#" className="text-gray-700 block px-4 py-2 text-sm" role="menuitem" id="menu-item-0">Account settings</a>
                        <a href="#" className="text-gray-700 block px-4 py-2 text-sm" role="menuitem" id="menu-item-1">Support</a>
                        <a href="#" className="text-gray-700 block px-4 py-2 text-sm" role="menuitem" id="menu-item-2">License</a>
                        <form method="POST" action="#" role="none">
                            <button type="submit" className="text-gray-700 block w-full text-left px-4 py-2 text-sm" role="menuitem" id="menu-item-3">Sign out</button>
                        </form>
                    </div>
                </div>
            </Transition>
        </div>
    );
}
```
_to learn more i am sharing my #buildinpublic journey on [twitter](https://twitter.com/zee_yass).
hope this was helpful, stay tunned for my upcoming serie on scaffolding authentication with Remix._