Next.jsで使い回せるコンポーネントを作ってみよう!

technologies

こんにちは株式会社ベンジャミンの市川です!

ときおり吹く木枯らしに、駆け足でやってくる冬の気配を感じるこのごろ、いかがお過ごしでしょうか。

今回は、Next.jsにおいて「一度作ったファイルを、様々なプロジェクトであっても、ひたすら使いまわして工数を削減する」というテーマで、MUIを例にして書いていきます。

(※) ライブラリを変更する場合は改修コストが発生しますが、既に設計されたファイルに当てはめていくだけでよいので、考える手間を少なく実装することができます

目次

環境情報

  • Next.js 13.4.8
  • MUI 5.13.7
  • TypeScript 5.1.6

完成形


1.Next.js(React)におけるコンポーネントを分ける考え方

Next.js(React)におけるコンポーネントの分け方には様々なものがあります。

例えば、UI設計寄りなパーツ・コンポーネント単位で分ける「Atomic Design」や、画面仕様書寄りな機能単位で分ける「bulletproof-react」などがありますが、結局どの手法であっても、一番再利用される回数の多い最小単位のUI(ボタンなど)については、ある程度考え方は共通しているのではないかと考えています。

2.実際に作成してみる

作成するにあたっていくつかルールがあります。

  • UIライブラリを親から直接importせずにラップしたコンポーネントを作成し利用する(例:Spinner.tsx)
    ※ コンポーネント名称は、名が体を表すような名称にする必要があります(各親コンポーネントから使いまわした時、名称の衝突等が起こってしまうと修正が必要なため)
  • 子コンポーネントのサイズ(width)・余白(margin)・位置(position)は設定しない ※例外あり。基本的には親から指定する。
  • ① 型名は <コンポーネント名> + Props (例:`SpinnerProps`)
  • ② 見た目を親から設定できるようにCSS引数を追加(例:`sx?: SxProps<Theme>;`)
  • ③ 見た目に関する引数はオプショナルかつ初期値を設定(例:`size = 40`)
  • ④ 上記②のCSS引数をコンポーネントのスタイル属性内でスプレッド構文を用いて展開(例:`sx={{ …sx }}`)

上記に沿ったコードを見てみましょう。

  • Spinner.tsx子コンポーネント
import { Theme } from '@emotion/react';
import { CircularProgress, SxProps } from '@mui/material';

export type SpinnerProps = { // ①
  size?: number;
  color?: string;
  sx?: SxProps<Theme>; // ②
};

                                                     // ↓↓↓③↓↓↓
export const Spinner = ({ size = 40, color = '#999999', sx = {} }: SpinnerProps) => {
  return (
    <CircularProgress
      size={size}
      sx={{ ...sx, color: color }} // ④
    />
  );
};

作成したコンポーネント Spinner.tsx を使って、数パターンの見た目で再利用してみます。

  • Spinner.tsxを利用した例 ※ 親コンポーネント
import { Box, Stack } from '@mui/material';
import { Spinner } from 'src/components/common/loader/Spinner';

const Page = () => {
  return (
    <main style={{ padding: '20px' }}>
      <Stack
        direction="column"
        spacing={4}
      >
        <Box>そのまま使ってみる</Box>
        <Spinner />

        <Box>色とサイズを変更してみる</Box>
        <Spinner
          color="#88ccbb"
          size={80}
        />

        <Box>色とスタイルを直接変更してみる</Box>
        <Spinner
          color="#bb88cc"
          sx={{ animationDuration: '16s' }}
        />
      </Stack>
    </main>
  );
};

export default Page;

【表示したイメージ】

いい感じに使いまわすことができました!

ほかにも、「もっといじれる項目の数を増やしたい」と思ったらオプショナル引数を追加していけばいいですし、「見た目をガラっと変えたい」と思ったら<CircularProgress ... />を変更するだけでよいので、Spinner.tsxを利用している親コンポーネントをいじることなく、変更箇所とスコープを狭めることができます。

あとがき

いかがでしたでしょうか。

個人的な意見ではありますが、コンポーネント設計さえきちんとできていれば、UIライブラリを変更したい、はたまたフレームワークをVue.jsにしたい、といったケースでも比較的応用しやすい(=迷いなくスピーディーに切り替えができる)のではないかと考えています。
特に最近はGitHub CopilotやChatGPTのおかげで、上記ケースのような実装コストは下がったと感じます。

以上、この記事が皆様のお役に立てれば幸いです。それでは。

関連記事一覧