社内勉強会用のメモ
概要
コンポーネントの概念でアプリケーションを組み立てる際に使えるデザインパターン3種類を紹介。
- HOC
- Render Props
- Compound Components
1. HOC
高階コンポーネント(Higher Order Component)の略称。
高階コンポーネント (higher-order component; HOC) はコンポーネントのロジックを再利用するための React における応用テクニックです。HOC それ自体は React の API の一部ではありません。HOC は、React のコンポジションの性質から生まれる設計パターンです。
具体的には、高階コンポーネントとは、あるコンポーネントを受け取って新規のコンポーネントを返すような関数です。
つまり、HOCとはコンポーネントの一種で、通常のコンポーネントがpropsを受け取ってjsxを返すのとは違い、コンポーネントを受け取ってコンポーネントを返す関数のことを指す。
基本的には以下の条件を満たす関数をHOCと呼ぶ。
- 副作用の無い純粋な関数である。
- 引数(の一つ)としてコンポーネントを受け取る。
- 引数で受け取ったコンポーネントをラップした(内包した)新しいコンポーネントを返す。
利用例
- react-reduxの
connect
関数 - Next.jsの
withRouter
関数 - next-redux-wrapperの
withRedux
関数
などなど。
HOCの名前には、慣習的にwith
を接頭辞としてつけることが多い。
有用性
横断的関心事に HOC を適用する
とあるように、コンポーネント間で横断の(=共通の)処理が存在しそれをDRYにしたい場合に使える。
おそらく HOC とコンテナコンポーネントと呼ばれるパターンの類似性に気づいたでしょう。コンテナコンポーネントは高レベルと低レベルの関心事の責任を分離する戦略の一部です。コンテナはデータ購読や state を管理してコンポーネントに props を渡し、渡された側のコンポーネントは UI の描画などの事柄を取り扱います。HOC はコンテナをその実装の一部として使用します。HOC をパラメータ化されたコンテナコンポーネントの定義であると考えることができます。
つまり、HOCはコンテナの一種と考えることができる。
簡単な実装例
type InjectedProps = {
isBraveThunders: boolean;
};
const withBraveThunders = function<CP>(Component: React.FunctionComponent<CP>) {
return (props: InjectedProps & CP) => {
const { isBraveThunders, ...passThroughProps } = props;
// isBraveThundersがtrueの場合は、ページ読み込み後に問答無用で川崎ブレイブサンダースの公式ページにリダイレクトする
React.useEffect(() => {
if (!isBraveThunders) return;
location.href = "https://kawasaki-bravethunders.com/";
}, [isBraveThunders]);
return <Component {...passThroughProps as any} />;
};
};
type ChildProps = {
hoge: number;
piyo: number;
};
const ChildComponent = (props: ChildProps) => (
<div>
<div>{props.hoge}</div>
<div>
<div>{props.piyo}</div>
</div>
</div>
);
withBraveThunders(ChildComponent);
ちなみにconnectとかは正確に言うと
export const SharedLiveSPContainer = connect(
mapStateToProps,
mapDispatchToProps,
)(SharedLiveSPComponent);
connect(options)(component)
という感じで「connect関数がHOCを返している」と言えるね。
細かい注意点とかはReactの公式ドキュメントも読もう。
2. Render Props
“レンダープロップ (render prop)”という用語は、値が関数である props を使って、コンポーネント間でコードを共有するためのテクニックを指します。
render-props.js<DataProvider render={data => ( <h1>Hello {data.target}</h1> )}/> const DataProvider = (props) => ( <div>{props.render({ target: "hoge" })}</div> );
jsxを返す関数をpropsとして受け取り、それをrender内で実行するコンポーネントのこと。
有用性
横断的関心事にレンダープロップを使う
HOCと同じで、コンポーネント間で共通の処理などに使う。
また、DIっぽいイメージでレンダリングする内容を外から受け取りたい場合にも使える。
レンダープロップの興味深い点として、多くの高階コンポーネント (HOC) がレンダープロップを使った通常のコンポーネントによって実装可能ということが挙げられます。
基本的にはHOCでできることはRender Propsでも実装できそう。
例ではprops名にrenderを使ってるけど、これは別にReactが用意している特別な何かなわけではないので、props名はなんでも良い。
renderだけだと意味がわからない場合は renderHoge
みたいな感じにしても良い。
つまり、children
というprops名でもOKで、Reactではchildrenというpropsはjsxで子要素として表現できるため、
<DataProvider>
{data => (
<h1>Hello {data.target}</h1>
)}
</DataProvider>
const DataProvider = (props) => (
<div>{props.children({ target: "hoge" })}</div>
);
さっきの例はこれでも良い。
react-virtualized(Reactで無限スクロールを実現するライブラリ)ではこの形が使われてる。
AutoSizer.js
// Render your list
ReactDOM.render(
{({ height, width }) => (
)}
,
document.getElementById('example')
);
https://github.com/bvaughn/react-virtualized/blob/master/docs/AutoSizer.md
---
https://ja.reactjs.org/docs/render-props.html
Reactのドキュメント読もう。
---
# 3. Compound Component
- [Reactのデザインパターン Compound Components](https://qiita.com/seya/items/d08b8a6c12f3e71d5971)
- [React.js — Compound Components](https://medium.com/@Dane_s/react-js-compound-components-a6e54b5c9992)
- [Quick guide to React compound components](https://blog.logrocket.com/guide-to-react-compound-components-9c4b3eb482e9/)
- [React Hooks: Compound Components](https://kentcdodds.com/blog/compound-components-with-react-hooks)
---
# 参考
https://kentcdodds.com/blog/advanced-react-component-patterns
コンポーネントのデザインパターン色々
- Compound Components
- Higher Order Components
- Render Props
- Prop Collections and Getters
- State Initializers
- Controlled Components
- Provider
後ほど色々見てこう。