2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【React】自作コンポーネントで超軽量タブ

Last updated at Posted at 2023-08-13

みなさん、こんにちは
まちゅです。

今回はReactで軽量のタブコンポーネント作成を行っていこうと思います。
外部ライブラリは使いません。

概要

このようなタブを作ります。
外部の不要なライブラリは一切使いません。
スタイルやアニメーションは皆さんのお好みで調整してください!Animation.gif

コード

まずは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で回しているタブのindextabが一致した判定をとって、
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画像に比べて微妙に挙動が滑らかになっています。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?