Reactで複数のアコーディオンの実装方法について

悩んでいる人

Reactでの複数のアコーディオンの実装ってどうやるのかな?
『よくある質問』みたいなのを実装したいんだけど。

本記事では、Reactでのアコーディオンの実装について解説していきます。

実際に動くコードも用意しておりますので、動作を確認しながら理解を深められるようになっております。

本記事の信頼性

30歳から異業種への転職をして、Shopify Experts企業で1年半ほどフルリモートで勤務していました。
現在は名古屋の自社開発企業のフロントエンドエンジニアしています。フリーランスとしても活動しています。

オカ

Reactもまだまだ勉強中なのでもし間違っているところなどあれば、
DMで教えてもらえるとうれしいのです。
Twitter: https://twitter.com/oka_jumboworld

スポンサーリンク

目次

最終の実装イメージとコード

あわせて読みたい
よくある質問 - CodeSandbox よくある質問 by okahanako09875 using react, react-dom, react-scripts
export default function App() {
  const [openAnswer, setOpenAnswer] = useState({ 0: false });
  const handleOpenAnswer = (index) => {
    setOpenAnswer((prevState) => ({
      ...prevState,
      [index]: !prevState[index]
    }));
  };

  return (
    <div className="App">
      <h1>アコーディオンの実装方法</h1>
      {data.faqs.map((faq, index) => {
        return (
          <div className="faq-item" key={index}>
            <div
              className="faq-item__title"
              onClick={() => handleOpenAnswer(index)}
            >
              {faq.question}
            </div>
            {openAnswer[index] ? (
              <div className="faq-item__description">{faq.answer}</div>
            ) : undefined}
          </div>
        );
      })}
    </div>
  );
}
data.json (よくある質問のデータ)
{
  "faqs": [
    {
      "question": "誕生日はいつですか?",
      "answer": "1990年4月12日生まれです。32歳です。(2022年11月時点)"
    },
    {
      "question": "なぜエンジニアを目指したのですか?",
      "answer": "簡単に1,000万円稼げると聞いたので目指しました。"
    },
    {
      "question": "エンジニアをしていて楽しいと感じるときはなんですか?",
      "answer": "思っていたようにフロントの実装ができたときです。バックエンドは現在勉強中なのですが、ロジックを考える時間も楽しいです。"
    },
    {
      "question": "これからどうなりたいですか?",
      "answer": "まずはフロントエンドを極めていきたいです。バックエンドやデザイン領域にも関心があるので自分で勉強は続けていきたいです。"
    }
  ]
}

コードの解説

よくある質問の表示部分

まずは、HTMLの作っている部分についてです。

{data.faqs.map((faq, index) => {
  return (
    <div className="faq-item" key={index}>
      <div
        className="faq-item__title"
        onClick={() => handleOpenAnswer(index)}
      >
        {faq.question}
      </div>
      {openAnswer[index] ? (
        <div className="faq-item__description">{faq.answer}</div>
      ) : undefined}
    </div>
  );
})}

data.jsonからインポートしたデータをJavaScriptのmap関数で配列を呼び出します。
data.faqs.map((faq, index)faqの中身を確認してみます。

質問(question)と回答(answer)の要素を取得して表示します。

<div className="faq-item" key={index}>
  <div className="faq-item__title">{faq.question}</div>
  <div className="faq-item__description">{faq.answer}</div>
</div>

これで画面部分の実装は完了です。

クリックしたときに表示・非表示を制御する

まずは、質問の要素をクリックしたときのイベントを取得します
console.logで確認する。

<div
  className="faq-item__title"
  onClick={() => handleOpenAnswer(index)}
>
  {faq.question}
</div>
const handleOpenAnswer = (index) => {
  console.log(`${index}番目をクリック!!`);
};

次の実装が本題の部分になってきます。

表示・非表示の状態管理についてはuseStateで行います。

  const [openAnswer, setOpenAnswer] = useState({ 0: false });
  const handleOpenAnswer = (index) => {
    setOpenAnswer((prevState) => ({
      ...prevState,
      [index]: !prevState[index]
    }));
  };

openAnswerがState変数、setOpenAnswerがStateを更新するための関数になります。

クリックしたときにsetOpenAnswer関数でオブジェクトを更新します。

prevStateには更新前のStateが入っているので最初の状態ですと{ 0: false }が入っています。

...prevState...とはスプレッド構文といいます。大括弧[]や中括弧{}を外してくれることで配列やハッシュ構造を展開してくれます。

クリックした要素ののboolean値を反転させてStateに保持するために[index]: !prevState[index]の記述を行います。

オカ

!prevState[index][index]が実はよく意味が分かっていない、、、。

これでopenAnswerの値をconsole.logで確認します。

あわせて読みたい
Hooks API Reference – React A JavaScript library for building user interfaces

あとは、条件分岐を追加することで表示・非表示を制御することが可能です。

ここでは、三項演算子で実装しています。

openAnswer[index]でどの質問なのかを判定して出し分けを行っております。

{openAnswer[index] ? (
<div className="faq-item__description">{faq.answer}</div>
) : undefined}

まとめ

なんとか実装はできても、100%挙動が理解できていないのが悔しいですがまずはOKとしましょう。(自分に甘い)

また、実はこの実装をTypeScriptでやっていたのですがそのときにかなりハマってしまったのでその記事もどこかで書ければと思っています。

オカ

Reactの学習のオススメの教材です^^

始めの一歩

ステップアップのための教材

スポンサーリンク

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
目次