Next.jsで使い回せるコンポーネントを作ってみよう!
こんにちは株式会社ベンジャミンの市川です!
ときおり吹く木枯らしに、駆け足でやってくる冬の気配を感じるこのごろ、いかがお過ごしでしょうか。
今回は、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のおかげで、上記ケースのような実装コストは下がったと感じます。
以上、この記事が皆様のお役に立てれば幸いです。それでは。