【スタイリング編】React Application Architecture for Production が勉強になりすぎる
開発環境
- react 18.2.0
- next 13.4.2
前提
今回の記事はReactのベストプラクティスと呼び声の高いbulletproof-reactで有名なAlan氏が2023年1月に公開した著書「React Application Architecture for Production」の紹介です。
ソースコードは公開されているので、こちらを眺めるだけでも学べることはたくさんあると思いますが、個人的に気になる部分を本記事含めいくつかの記事に分けて紹介します。
※上記で記した開発環境のバージョンは実際に動作確認した際に利用したバージョンです。著者で紹介されているバージョンと異なりますのでご了承ください。
本題
今回はスタイリングについて汎用的に利用することの多いButtonコンポーネントを例にあげて紹介します。
著書ではChakraUIを利用していますが、根本的な考え方はどのライブラリを利用していても同じです。
import { Button as ChakraButton } from '@chakra-ui/react';
import type { MouseEventHandler, ReactNode } from 'react';
const variants = {
solid: {
variant: 'solid',
bg: 'primary',
color: 'primaryAccent',
_hover: {
opacity: '0.9',
},
},
outline: {
variant: 'outline',
bg: 'white',
color: 'primary',
},
};
export type ButtonProps = {
children: ReactNode;
type?: 'button' | 'submit' | 'reset';
variant?: keyof typeof variants;
isLoading?: boolean;
isDisabled?: boolean;
onClick?: MouseEventHandler<HTMLButtonElement>;
icon?: JSX.Element;
};
export const Button = ({
variant = 'solid',
type = 'button',
children,
icon,
...props
}: ButtonProps) => {
return (
<ChakraButton
{...props}
{...variants[variant]}
type={type}
leftIcon={icon}
>
{children}
</ChakraButton>
);
};
重要なのはvariantsオブジェクトです。
Buttonコンポーネントの中であらかじめ利用できるスタイルをsolid, outlineのように定義しています。
利用する側はどのスタイルを利用するか、だけを決めることになります。
重要なのは利用する側はスタイルの詳細を変更できないことです。
こうすることで、汎用性を保ちつつ、開発者ごとの微妙なスタイルずれがおきなくなります。
よくあるダメなパターンとして、利用側からスタイル(tailwindCSSの場合だとclassNameを渡すなど)をButtonコンポーネントに渡すパターンです。
これは一見使い勝手が良いように見えますが、色々やりすぎます。
パディングや文字サイズなど、微妙に個人の感覚でずれてしまい、結果的に統一感のないレイアウトになることもあります。
また、そのように使い所ごとにカスタマイズしていくと、他の場所でも同じスタイルを利用したい、という場面で同じスタイルを他の場所で書いて・・・のようなことにもなります。
汎用的なコンポーネントは自由すぎるとまとまりがなくなっていくことが多いので、variantsのような実装方法はとても有効です。
利用する側は以下のようにシンプルな実装で済みます。
<Button
isDisabled={false}
isLoading={false}
variant="outline"
onClick={() => null}
>
Click
</Button>
参考までに、tailwindCSSだとこんな感じで利用するイメージになります。
const variants = {
primary: 'bg-blue-500 text-white hover:bg-blue-600 active:bg-blue-700',
secondary: 'bg-green-500 text-white hover:bg-green-600 active:bg-green-700',
inverse: 'bg-white text-gray-700 hover:bg-gray-100 active:bg-gray-200'
}
さいごに
汎用コンポーネントのスタイルに関わらず、汎用的なコンポーネントで詳細を定義し、利用側は指定するだけ、のような考え方は開発効率が上がるのでおすすめです。