ReactでUIライブラリに依存しないクリーンなアーキテクチャにするのを試してみようと思って、MaterialUI(以下、MUI)のコンポーネントをラップした自作コンポーネントの作成を試してみた。
狙い
MUIは便利なライブラリだが、スタイルをうまくつけられない・動きを柔軟に作りたいなどの要望が後々出るなどして変えたくなることがありそうな予感はする(フロントエンド開発経験は多くないので予感でしかない)。 そこで、MUIのコンポーネントをそのまま使うのではなく、自前のコンポーネントでラップし、MUIのインターフェースが実装内のインターフェースに漏れ出ないようにすれば置き換えたくなったときに変更を最小限にできるのではないかと考えた。
作ってみた
atomicレベルのコンポーネントとして、Selectコンポーネントを作ってみる。
import React, { ReactNode } from "react"; import { Select as MuiSelect } from "@mui/material"; export type OnChangeEventHandler = ( e: { target: { value: string; name: string } }, child: ReactNode ) => void; export type SelectProps = { id: string; labelId: string; label: string; value: any; onChange: OnChangeEventHandler; className: string; }; const Select: React.FC<SelectProps> = (props: SelectProps) => { return ( <MuiSelect id={props.id} labelId={props.labelId} value={props.value} onChange={props.onChange} label={props.label} className={props.className} ></MuiSelect> ); }; export default Select;
考察
- MUIで許容されているPropsのうち、使いたいフィールドのみを指定できる。これにより、インターフェースがシンプルになり、制約が効くようになる。Good
- 制約されるため、あとからPropsを増やしたくなったときの影響が大きくなりそう。NG
- OnChangeで受け付けている型がReact(HTML)での
<select>
のonChangeで受け付けているEventHandlerとは異なるので一般化にハードルがある。NG
MUIで使用可能なPropsを理解して、開発しているアプリケーションで契約したいインターフェースからMUIのインターフェースへのうまくつなげるのけっこう難しそうな印象。単に自分の理解度が浅いだけかもしれないが。 少なくとも現時点では、クリーンにすることを最初からがんばるのは得策ではないと思う。