みなさん、こんにちは
まちゅです。
今回はReactで軽量のタブコンポーネント作成を行っていこうと思います。
外部ライブラリは使いません。
概要
このようなタブを作ります。
外部の不要なライブラリは一切使いません。
スタイルやアニメーションは皆さんのお好みで調整してください!
コード
まずはtsxからです。
interface Props {
onChange: (tab: number) => void | (() => void);
}
// ダミーデータ
const contents = [
{ text: "幼児", path: "/games" },
{ text: "1年生", path: "/gallery" },
];
export const Tabs: FC<Props> = ({ onChange }) => {
const [tab, setTab] = useState<number>(0);
return (
<div className={style["contents-tab"]}>
<div className={style["contents-tab-button-wrapper"]}>
{contents.map((content, index) => (
<div
onClick={() => {
setTab(index);
onChange(tab);
}}
className={
index === tab
? style["contents-tab-button-selected"]
: style["contents-tab-button"]
}
>
{content.text}
</div>
))}
</div>
<div className={style["contents-tab-underline"]} />
</div>
);
};
軽い解説です。
interface Props {
onChange: (tab: number) => void | (() => void);
}
をFCの型引数に渡しておき、
タブが切り替わった際のコールバック関数を
FC
の引数で受け取れるようにします。
const [tab, setTab] = useState<number>(0);
では、tab
に開いているコンポーネント番号を記憶させます。
onClick={() => {
setTab(index);
onChange(tab);
}}
ここで
setTab(index)
で現在のタブ番号を保存しながら、
コールバックの引数にタブ番号を渡してあげます。
Array.map
で回しているタブのindex
とtab
が一致した判定をとって、
classNameを切り替えています。
ポイントは、コールバック関数を受け取れるようにしているということです。
タブ番号を取得するためにDispatch<SetStateAction<number>>
を
interfaceに定義する設計は複雑度が増すのでナンセンスです。
また、今回利用するscssは以下。
.contents-tab {
width: 672px;
padding-bottom: 2rem;
&-button-wrapper {
display: flex;
gap: 0.5rem;
}
&-button,
&-button-selected {
width: 144px;
height: 48px;
border: $blue-1 solid;
border-radius: 0.5rem 0.5rem 0 0;
border-width: 2px 2px 0px 2px;
display: grid;
place-items: center;
font-weight: bold;
cursor: pointer;
transition: 0.2s;
}
&-button {
color: $blue-1;
}
&-button-selected {
background-color: $blue-1;
color: $white-1;
}
&-underline {
height: 2px;
background-color: $blue-1;
}
}
利用する側では、このように使います。
const [tabNum, setTabNum] = useState<number>(0);
const tabHandler = (tab: number) => {
setTabNum(tab);
};
~~(略)~~
<ContentsTab onChange={tabHandler} />
基本的に、tabHandler
関数内でタブの内容変更処理を呼び出します。
コールバックを渡して処理してもらうという事を覚えましょう。
もう少し高度な設計の話で、React Context
を用いる方法もあり。
そちらの方が自由度は高いですが、それはまたの機会に書きます。
さいごに
こちらいかがだったでしょうか。
react-tabs
などライブラリがありますが、
10分~20分もあれば書ける内容ですね。
よりよい手法があればコメントいただければ幸いです。
(追記)
cssのtransitionを追加したので、
gif画像に比べて微妙に挙動が滑らかになっています。