abeshi blog
カテゴリーで検索

Reactで表示/非表示ボタンのコンポーネントを作ろう!

2021年11月6日

今回作成する表示/非表示を切り替えるコンポーネント


今回はこのようなよくある表示/と非表示を切り替えるボタンを作成していきます。




よく使うものはコンポーネントにしてどんな状況でも使いまわせることが大事です。
では早速やっていきましょう!!

使用技術


React, TypeScript, css modules, react hooks


ファイルの作成



今回はDisplaySwitchingButtonというコンポーネント名で作成していきます。

下記二つのファイルを使用していきます。
DisplaySwitchingButton.tsx
displaySwitchingButton.module.scss

今回はスタイルはcss modulesを使用しています。

アイコンも必要なので追加します。
こちらはコピペして使用してください!

// src/assets/icons/PlusIcon.tsx

// - フレームワーク =======================================================================================================
import React, { memo, VFC } from "react";

/* eslint-disable-next-line react/display-name */
export const PlusIcon: VFC = memo(() => {
  return (
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" xmlSpace="preserve">
      <g transform="translate(-.3)">
        <circle cx="10.3" cy="10" r="10"/>
        <path d="m11.12 14.2-.07-3.46h3.4c.42 0 .84-.42.84-.84a.91.91 0 0 0-.85-.85h-3.4v-3.4a.91.91 0 0 0-.84-.85.91.91 0 0 0-.85.85v3.4h-3.4a.91.91 0 0 0-.84.85c0 .42.42.84.85.84h3.4v3.4c0 .42.42.85.84.85.5.07.85-.29.92-.78z" fill="#fff"/>
      </g>
    </svg>
  );
});


// src/assets/icons/MinusIcon.tsx

// - フレームワーク =======================================================================================================
import React, { memo, VFC } from "react";

/* eslint-disable-next-line react/display-name */
export const MinusIcon: VFC = memo(() => {
  return (
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 25">
      <g transform="translate(-1201 -399)">
        <circle cx="1213.5" cy="411.5" r="12.5"/>
        <path fill="#fff" d="M1208.8 411h10.2v1h-10.2z"/>
      </g>
    </svg>
  );
});


これで準備は完了です。


コンポーネントをどう実装するか?



今回のやりたいこととしては下記画像のようになります。



クリックイベントによって表示非表示を切り替える必要があるのでstateにbooleanの値を保持する必要がありそうです。
また、どの要素が来ても文言を変えられるよう"XXXXを表示"のXの部分は動的にした方がよさそうです。→こちらはpropsで渡していきます。

そして表示/非表示にする要素に関してはchildrenとしてコンポーネントの中に包むようにします。(こちらは意味が分からなくても実装時説明します。)


コンポーネントのコード


// src/components/atoms/DisplaySwitchingButton/DisplaySwitchingButton.tsx

// - フレームワーク =======================================================================================================
import React, { memo, ReactNode, useState, VFC } from "react";

// - アセット ============================================================================================================
import styles from "./displaySwitchingButton.module.scss";
import { PlusIcon } from "../../../assets/icons/PlusIcon";
import { MinusIcon } from "../../../assets/icons/MinusIcon";


type Props = {
  label: string;
  children: ReactNode;
  style?: React.CSSProperties;
}

/* eslint-disable-next-line react/display-name */
export const DisplaySwitchingButton: VFC<Props> = memo((props) => {

  const { label, children, style } = props;

  const [ displayChildren, setDisplayChildren ] = useState<boolean>(true);

  const onClickDisplayChildren = (): void => {
    setDisplayChildren(!displayChildren);
  }

  return (
    <div className={styles.displaySwitchingButton} onClick={onClickDisplayChildren} style={style}>

      { displayChildren ? (
        <div className={styles.flexContainer}>
          <div className={styles.label}>{ `${label}を非表示` }</div>
          <div className={styles.icon}>
            <MinusIcon/>
          </div>
        </div>

      ) : (

        <div className={styles.flexContainer}>
          <div className={styles.label}>{ `${label}を表示` }</div>
          <div className={styles.icon}>
            <PlusIcon/>
          </div>
        </div>

      )}

      { displayChildren && <div>{ children }</div> }
    </div>
  );
});


説明していきます。

type Props = {
  label: string;
  children: ReactNode;
  style?: React.CSSProperties;
}


まずはPropsの型定義をしています。
labelは動的の文言の部分です。
childrenの型定義はReactNodeです。(これはお決まりまので覚えておきましょう。)

childrenとは



コンポーネントの中に入る子要素は事前には知らないものがあります。
例えば今回で言えば表示/非表示にする要素は事前に知ってはいけません。



今回はプロフィールを入れる例で最初に説明しましたが、これはもしかしたらカテゴリーが入る可能性もあれば、ブログ記事が入る可能性もあるのです。
そういった不確定な要素を定義するのがchildrenです。

コンポーネントの子要素に挟むとchildrenになります。

<DisplaySwitchingButton >
  <ProfileCard targetProfile={myProfile} />  ← DisplaySwitchingButtonで挟むとchildren になる
</DisplaySwitchingButton>


詳しくはこちらで確認しましょう。
https://ja.reactjs.org/docs/composition-vs-inheritance.html

そして表示/非表示のstateをuseStateで定義しておきます。

 const [ displayChildren, setDisplayChildren ] = useState<boolean>(true);


あとはこちらをクリックイベントに合わせて切り替えます。
下記の部分です。

  const onClickDisplayChildren = (): void => {
    setDisplayChildren(!displayChildren);
  }
  <div className={styles.displaySwitchingButton} onClick={onClickDisplayChildren}>



あとは簡単です。
ロジックで言うと
displayChildrenがtrueであれば表示, falseであれば非表示です。

こちらを三項演算子で切り替えています。

{ displayChildren ? (trueの場合の処理) : (falseの場合の処理)


そして最後にdisplayChildrenがtrueの場合はchildrenを表示しています。

{ displayChildren && <div>{ children }</div> }


&&は
左辺がtrueであれば && 右辺を返す
です!


スタイルの調整



最後にスタイルを調整しましょう。

.displaySwitchingButton {

  .flexContainer {

    display: flex;
    align-items: center;
    column-gap: 10px;

    cursor: pointer;


    &:hover {

      text-decoration: underline;
    }


    &:active {

      opacity: 0.7;
    }


    &:focus {

      text-decoration: underline;
    }
  }



  .icon {

    width: 20px;
    height: 20px;

    fill: #423A57;
  }
}



これで完成です!

コンポーネントの呼び出し



呼び出す際は切り替えたい要素をchildrenに入れます。


<DisplaySwitchingButton label={"プロフィール"}>
  <ProfileCard targetProfile={myProfile} />
</DisplaySwitchingButton>



お疲れ様でした!!!


今回のソースコード


github