Detecting click outside component using React hooks

If you have tried developing your own dropdown, modal, or popover in React, you would have come across this. “How do I detect a click outside my react component so that I can close it?” Detecting click outside component is luckily is not that difficult. This post will use react hooks to implement this functionality.

Before we get started with our process of detecting click outside component using React hooks, there is one hook in particular that we need to know about: useRef.

The useRef hook

- Advertisement -

useRef is a react hook that can be used to access DOM elements. It returns a mutable object whose current property is initialized to the argument that gets passed as an argument.

The syntax for using the hooks looks like this:

const refContainer = useRef(initialValue);

And a sample implementation to focus the component on render would be:

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

ref can be used in element tags or components too. It provides a way to access the corresponding DOM nodes. If you pass in a reference object using ref = {}, React sets the current property of the corresponding node. This property is updated whenever the node changes too.

There are other use cases for the useRef hook as well. But this post will be focused only on detecting click outside component. If you are interested in knowing more about what the useRef hook can do, do check out our post regarding that hook.

Setting up event listeners

Now that you know of useRef, we will use it along with an event listener (for mouseDown or click). This listener will be attached to the document whenever the component is rendered. It will also be unmounted whenever the component is hidden. For obtaining this functionality, the useEffect react hook can be used. If you want a deeper insight into the useEffect react hook, you can read the previous post about it.

For the component, there is a showOptionsList variable that is being used as a state variable to determine whether the component is visible or not. Thus, the useEffect will have it as a dependency, and according to its value, the event listener will be added/removed.

 useEffect(() => {
    if (showOptionsList) {
      document.addEventListener('mousedown', handleClickOutside);
    } else {
      document.removeEventListener('mousedown', handleClickOutside);
    }
    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
    };
  }, [showOptionsList]);

Detecting click outside component

Now that the handleClick will be triggered every time a click is registered on the document, all that remains is to check if the click is outside the component or not. For this, a reference to the component is needed. This can be obtained by making use of the useRef hook that was discussed earlier. Thus:

const Select = () => {
  const node = useRef();
  return (
    <div ref={node}>
      // Remaining code
    </div>
  );
};

And then, all that needs to be done in the handle click outside function would be to check if the user clicked outside the component or not. An implementation could be to check the target element of the click and if that equals the reference. But that only works for a single level node. In the case of multiple sub-nodes, the simple comparison would not work.

The .contains() method can be used to solve that problem. It tells if a node is a child of a given node or not. Thus, the implementation of the function becomes:

const handleClickOutside = (e) => {
  if (node.current && node.current.contains(e.target)) {
    // inside click
    return;
  }
  // outside click
  setShowOptionsList(false);
};

The completed source code for the Select implementation can be found on Github if you want to go through it. If you want to make yourself familiar with other react hooks like useState and useEffect, useReducer, or useContext, check out the respective posts.

If there are any other react hooks related things that you would want to cover, or if you have any queries, feel free to drop a comment below.

Recent Articles

Chrome devtools: Using logpoints for logging messages directly

When it comes to debugging JavaScript in Chrome devtools, there are two different camps: the console.log fans and the debugger/breakpoint maximalist. I...

How to prevent npm install for unsupported Node.js versions

npm configurations allow us to do quite a lot of nifty things. One of them is to allow the project to set...

How to terminate a process on a port using the command line

Zombie processes are usually a pain to figure out. More often than not, I end up googling about how to terminate a...

Detecting dark mode preference using JavaScript

As dark themes have become popular across the web and across operating systems, we might want to check the user's operating system...

JavaScript: Split string and keep the separators

String.prototype.split() is a valuable method to split strings based on a delimiter. There often comes a scenario when we want to split...

Related Stories

Leave A Reply

Please enter your comment!
Please enter your name here

Hi there! Want some more knowledge?

Think that the knowledge shared is helpful? You might want to give our mailing list a try. We'll send you 2-4 emails a month, right when new posts come out.

Hi there! Want some more knowledge?

Think that the knowledge shared is helpful? You might want to give our mailing list a try. We'll send you 2-4 emails a month, right when new posts come out.