React Hooks で作る要素の外側をクリックすると閉じるドロップダウン

はじめに

こんにちは。親から送られてくる家庭菜園の動画に癒されている kimizuy です。

今回は要素の外側をクリックすると閉じるドロップダウンのコンポーネントを React Hooks で実装した例を紹介します。

紹介するコンポーネントは以下のような動きになります。

  • 「Toggle dropdown」を押すとドロップダウン(メニューリスト)を表示する
  • ドロップダウンの外側をクリックするとドロップダウンが閉じる

Codesandbox のデモも用意しました。実際に触って試してみてください。

tldr

さっそくドロップダウンコンポーネントの実装例を紹介します。

useState でドロップダウンの表示・非表示の状態を管理しています。また useRef でドロップダウンの要素を参照し、useEffect で要素外のクリックを検知することでメニューを閉じられるようにしました。

function Dropdown() {
  const [showDropdown, setShowDropdown] = useState(false);
  const dropdownListRef = useRef<HTMLUListElement>(null);

  useEffect(() => {
    const handleClickToCloseDropdown = (event: any) => {
      const element = dropdownListRef.current;
      if (!showDropdown || element?.contains(event.target)) return;
      setShowDropdown(false);
    };

    window.addEventListener("click", handleClickToCloseDropdown);
    return () => {
      window.removeEventListener("click", handleClickToCloseDropdown);
    };
  }, [showDropdown, dropdownListRef]);

  return (
    <div className="DropdownContainer">
      <button
        type="button"
        onClick={() => {
          setShowDropdown((prevState) => !prevState);
        }}
      >
        Toggle dropdown
      </button>

      {showDropdown && (
        <ul ref={dropdownListRef} className="DropdownList">
          <li className="Item">
            <button onClick={() => console.log("item 1 clicked!")}>
              item 1
            </button>
          </li>
          <li className="Item">
            <button onClick={() => console.log("item 2 clicked!")}>
              item 2
            </button>
          </li>
        </ul>
      )}
    </div>
  );
}

おわりに

今回は React Hooks でドロップダウンコンポーネントを実装してみました。このテクニックは任意の要素の外側や内側のクリックイベントなどを検知して何かの処理を実行したくなったときに応用できそうですね。

以上、お読みいただきありがとうございました。

Gaji-Laboでは、React経験が豊富なフロントエンドエンジニアを募集しています

弊社ではReactの知見で事業作りに貢献したいフロントエンドエンジニアを募集しています。大きな制作会社や事業会社とはひと味もふた味も違うGaji-Laboを味わいに来ませんか?

もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください。お仕事お問い合わせや採用への応募、共に大歓迎です!

求人応募してみる!

投稿者 Yamasaki Kimizu

React, Redux, TypeScript プロジェクトでフロントエンド領域を担当。個人でも Next.js アプリの開発をしています。日課はRSSで取得した技術記事を読むこと、最近の関心は Core Web Vitals です。将来はでかい犬が飼いたいです。