ReactのProp CollectionsとProp Gettersについて
今回はReactのデザインパターンであるProp CollectionsとProp Gettersについて整理します。書いたり見たことあるようなパターンでしたが、それらに名前があることを最近知りました。パターンの名前を知ることで、自分の中で定着しやすくなりますし、チームメンバーと共通認識としてコミニケーションが取りやすくなりますね。
Prop Collections
Prop Collectionsは、複数のpropsをひとつのオブジェクトにまとめてコンポーネントに渡すデザインパターンです。一つのオブジェクトにまとめることで、コードの重複をなくし再利用しやすくなります。
使用例
例として、Modalコンポーネントに対してProp Collectionsを適用してみます。Modalコンポーネントには複数のpropsを渡す必要があります。Modalコンポーネントの呼び出し元で全てのpropsを書くのは面倒ですね。
<Modal
isOpen={true}
disabled={false}
title="新規登録"
content="登録しますか?"
className="modal"
nextButtonText="登録"
cancelButtonText="閉じる"
onNext={()=>{}}
onCancel={()=>{}}
onClose={()=>{}}
size="large"
hasFooter={true}
hasHeader={true}
/>
ここでProp Collectionsを利用して、propsをbasePropsとしてまとめます。
const baseProps = {
isOpen: true,
disabled: false,
title: '新規登録',
content: '登録しますか?',
className: 'modal',
nextButtonText: '登録',
cancelButtonText: '閉じる',
onNext: ()=>{},
onCancel: ()=>{},
onClose: ()=>{},
size: 'large',
hasFooter: true,
hasHeader: true
}
Modalコンポーネントには、basePropsをスプレッド構文で渡すだけで良くなります。見通しが良くなりますね。
<Modal {...baseProps} />
特定のpropsを変更したければ、basePropsをスプレッド構文で展開した後に、特定のpropsだけ上書きすることができます。
<Modal {...baseProps} size="small" />
また、basePropsからsmallModalPropsを定義して、それをModalコンポーネントに渡すこともできます。こうするとsmallModalPropsを別の場所で再利用できます。
const smallModalProps = {
...baseProps,
size: 'small'
}
<Modal {...smallModalProps} />
Prop Getters
Prop Gettersはpropsを返す関数を定義し、それ利用してコンポーネントにpropsを渡すデザインパターンです。Prop Collectionsでプロパティをカスタマイズする際、{...baseProps,size: 'small'}
のようにスプレッド構文を利用するとプロパティが上書きされてしまいます。そのため、Prop Collectionsだけでは特定のプロパティのデフォルト処理を維持しつつカスタマイズ処理を追加したい等のケースに対応できませんでした。Prop Gettersを利用すると柔軟にpropsをカスタマイズできます。
使用例
Prop GettersとしてgetModalProps
関数を定義し、先ほどのModalコンポーネントに渡すpropsを返します。このままだとpropsを返しているだけで処理をカスタマイズできません。
const getModalProps = (props) => {
return {
...props,
}
}
<Modal {...getModalProps(baseProps)} />
まず処理を合成するためのcompose
関数を定義します。compose
関数は、複数の関数を引数として受け取り、それらを配列として順次実行する関数を返します。
const compose =
(...funcs) =>
(...args) =>
funcs.forEach(fn => fn?.(...args))
例えば、以下のようにadd
関数とmultiply
関数を合成したmathOperations
関数を作成します。
const add = (x, y) => console.log(x + y);
const multiply = (x, y) => console.log(x * y);
const mathOperations = compose(add, multiply);
この時、mathOperations
関数は以下のようになり、mathOperations
関数に引数を与えて実行すると、その引数でadd
関数とmultiply
関数が順次実行されます。
const mathOperations = (...args) =>[add,multiply].forEach(fn => fn?.(...args));
mathOperations(2,5)
//Output:
// 7
// 10
getModalProps
関数に戻ってonNext
のデフォルト処理として()=>alert("ご登録ありがとうございます")
を定義します。次にreplacementOnNext
として受け取ったonNext
の追加処理をcompose
関数を利用して合成し、それを返します。
const getModalProps = ({ onNext: replacementOnNext, ...props }) => {
const defaultOnNext = () => {
alert('ご登録ありがとうございます')
}
return {
onNext: compose(replacementOnNext, defaultOnNext),
...props
}
}
この状態でonNext
に追加したい処理(() => alert('追加処理!')
)を渡してあげると、onNext
実行時に() => alert('追加処理!')
、()=>alert("ご登録ありがとうございます")
が順番に実行されます。
<Modal {...getModalProps({ ...baseProps, onNext: () => alert('追加処理!') })} />
このようにProp Gettersを使うと、コンポーネントに渡すpropsを纏めつつ特定のプロパティの処理を柔軟に変更することできます。propsとして渡すイベントハンドラにデフォルト処理としてevente.preventDefault()
やログ送信処理を仕込みたい時などに使えそうですね。
参考
Discussion