React でボタンをクリックすると、内容が表示されるアコーディオンを実装します。
まずは、アコーディオンで表示するタイトルと内容を配列で作成します。
jsx
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の中でmenusをmapで展開します。
jsx
return (
<>
<ul className="space-y-2">
{menus.map((menu, index) => (
))}
</ul>
</>
);
mapの中でliタグを作成します。
liの中で、ボタンとコンテンツの内容を作成します。
jsx
<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を使用します。
jsx
import React, { useState } from "react";
初期時は、どのコンテンツも開かないようにするために、index番号が該当しない数値にします。とりあえず今回は 3 つしかないので、該当しない番号である 100 を設定しました。
コンテンツが 200 ある場合は、useStateの中を 500 くらいにしましょう。
jsx
const [clicked, setClicked] = useState(100);
buttonのonClickにhandleClickを設定します。
handleClickには、indexを渡すようにします。
jsx
<button
type="button"
onClick={() => handleClick(index)}
style={{
width: "100%",
textAlign: "center",
backgroundColor: "#ff8c00",
color: "#f1f1f1",
padding: "0.5rem",
}}
>
handleClickを作成します。
handleClickを実行すると、setClickedにindexを渡すようにします。
jsx
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の番号が一致する場合、初期値に戻すようにします。
jsx
const handleClick = (index) => {
if (clicked === index) {
return setClicked(100);
}
setClicked(index);
};
ボタンの実装が完了したので、次は、コンテンツの表示・非表示を行います。
非表示の場合は高さを 0 にし、表示する場合はコンテンツの中身に合わせて高さを変えていきます。
コンテンツの中身の高さを取得するために、useRefを使用します。
reactからuseRefをインポートします。
jsx
import React, { useRef, useState } from "react";
useRefでcontentElを作成します。
jsx
function App() {
const contentEl = useRef();
buttonの下のdivにref属性を指定します。
jsx
<div ref={contentEl}>{menu.content}</div>
クリックしたボタンのindex番号とclickedの番号が一致する場合、heightにcontentElのcurrent.scrollHeightを指定します。
逆に一致しない場合、heightを 0 にし、overflowをhiddenにします。
jsx
<div
ref={contentEl}
style={
clicked === index
? {
height: contentEl.current.scrollHeight,
backgroundColor: "#e7d0a9",
}
: { height: "0px", overflow: "hidden" }
}
>
{menu.content}
</div>
一通り完成したので、動作確認します。
試しに 6 月 30 日をクリックすると、
6 月 30 日のコンテンツのみ表示されました。
7 月 2 日をクリックすると、
6 月 30 日のコンテンツが非表示になり、7 月 2 日のコンテンツが表示されました。
もう一度、7 月 2 日をクリックしてみると、
全ての内容が非表示になりました。