Reactでボタンをクリックすると、内容が表示されるアコーディオンを実装します。

まずは、アコーディオンで表示するタイトルと内容を配列で作成します。

import React from "react";

function App() {
  const menus = [
    { title: "6月30日", content: "晴れ" },
    { title: "7月1日", content: "曇り" },
    { title: "7月2日", content: "曇り時々晴れ" },
  ];

  return (
    <>
    </>
  );
}

export default App;

return内に、ulタグを作成します。

ulの中でmenusmapで展開します。

return (
  <>
    <ul className="space-y-2">
      {menus.map((menu, index) => (
      ))}
    </ul>
  </>
);

mapの中でliタグを作成します。 liの中で、ボタンとコンテンツの内容を作成します。

<ul className="space-y-2">
  {menus.map((menu, index) => (
    <li key={index} style={{ width: "240px" }}>
      <button
        type="button"
        onClick={}
        style={{
          width: "100%",
          textAlign: "center",
          backgroundColor: "#ff8c00",
          color: "#f1f1f1",
          padding: "0.5rem",
        }}
      >
        {menu.title}
      </button>
      <div>
        {menu.content}
      </div>
    </li>
  ))}
</ul>

クリックすると、コンテンツ内容が表示されたり非表示になったりします。

どのボタンを押したかを判別するには、indexを使い、押されたindex番号とコンテンツ内のindex番号が一致すると開くようにします。

押されている番号を状態管理するために、useStateを使用します。

import React, { useState } from "react";

初期時は、どのコンテンツも開かないようにするために、index番号が該当しない数値にします。とりあえず今回は3つしかないので、該当しない番号である100を設定しました。

コンテンツが200ある場合は、useStateの中を500くらいにしましょう。

const [clicked, setClicked] = useState(100);

buttononClickhandleClickを設定します。

handleClickには、indexを渡すようにします。

<button
  type="button"
  onClick={() => handleClick(index)}
  style={{
    width: "100%",
    textAlign: "center",
    backgroundColor: "#ff8c00",
    color: "#f1f1f1",
    padding: "0.5rem",
  }}
>

handleClickを作成します。

handleClickを実行すると、setClickedindexを渡すようにします。

function App() {
  const [clicked, setClicked] = useState(100);

  const menus = [
    { title: "6月30日", content: "晴れ" },
    { title: "7月1日", content: "曇り" },
    { title: "7月2日", content: "曇り時々晴れ" },
  ];

  const handleClick = (index) => {
    setClicked(index);
  };

ボタンを2回押すと閉じるようにしたいので、クリックしたボタンのindex番号とclickedの番号が一致する場合、初期値に戻すようにします。

const handleClick = (index) => {
  if (clicked === index) {
    return setClicked(100);
  }

  setClicked(index);
};

ボタンの実装が完了したので、次は、コンテンツの表示・非表示を行います。

非表示の場合は高さを0にし、表示する場合はコンテンツの中身に合わせて高さを変えていきます。

コンテンツの中身の高さを取得するために、useRefを使用します。

reactからuseRefをインポートします。

import React, { useRef, useState } from "react";

useRefcontentElを作成します。

function App() {
  const contentEl = useRef();

buttonの下のdivref属性を指定します。

<div ref={contentEl}>
  {menu.content}
</div>

クリックしたボタンのindex番号とclickedの番号が一致する場合、heightcontentElcurrent.scrollHeightを指定します。

逆に一致しない場合、heightを0にし、overflowhiddenにします。

<div
  ref={contentEl}
  style={
    clicked === index
      ? {
          height: contentEl.current.scrollHeight,
          backgroundColor: "#e7d0a9",
        }
      : { height: "0px", overflow: "hidden" }
  }
>
  {menu.content}
</div>

一通り完成したので、動作確認します。

image2

試しに6月30日をクリックすると、

image3

6月30日のコンテンツのみ表示されました。

7月2日をクリックすると、

image4

6月30日のコンテンツが非表示になり、7月2日のコンテンツが表示されました。

もう一度、7月2日をクリックしてみると、

image5

全ての内容が非表示になりました。

ブログ一覧