<div id="app"></app>
.outside {
  width: 100vw;
  height: 100vh;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.content {
  width: 200px;
  height: 200px;
  background-color: teal;
  margin: auto;
  display: flex;
  align-items: center;
  justify-content: center;
}
const { useRef, useEffect, useState } = React;
function OutsideWrapper() {
  return (
    <div className="outside">
      <Content />
    </div>
  );
}

function Content() {
  const [clicked, setClicked] = useState(false);
  const ref = useRef(null);
  useOutsideClickDetector(ref, () => {
    setClicked(false);
  });

  return (
    <div className="content" onClick={() => setClicked(true)} ref={ref}>
      {clicked ? "Click Outside" : "Click Me"}
    </div>
  );
}

function useOutsideClickDetector(
  ref: React.RefObject<Node>,
  handler: (event: MouseEvent) => void
) {
  useEffect(() => {
    function handleClickOutside(event: MouseEvent) {
      if (ref.current && !ref.current.contains(event.target as Node)) {
        handler(event);
      }
    }

    document.addEventListener("click", handleClickOutside);
    return () => {
      document.removeEventListener("click", handleClickOutside);
    };
  }, [ref]);
}

ReactDOM.render(<OutsideWrapper />, document.getElementById("app"));
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js
  3. https://unpkg.com/@types/react@16.13.1/index.d.ts
  4. https://unpkg.com/@types/react-dom@16.13.1/index.d.ts