LoginSignup
31
29

More than 3 years have passed since last update.

Reactで使う有名なコンポーネントデザインパターン

Last updated at Posted at 2019-09-19
1 / 20

社内勉強会用のメモ


概要

コンポーネントの概念でアプリケーションを組み立てる際に使えるデザインパターン3種類を紹介。

  1. HOC
  2. Render Props
  3. Compound Components

1. HOC

高階コンポーネント(Higher Order Component)の略称。

高階コンポーネント (higher-order component; HOC) はコンポーネントのロジックを再利用するための React における応用テクニックです。HOC それ自体は React の API の一部ではありません。HOC は、React のコンポジションの性質から生まれる設計パターンです。

具体的には、高階コンポーネントとは、あるコンポーネントを受け取って新規のコンポーネントを返すような関数です。


つまり、HOCとはコンポーネントの一種で、通常のコンポーネントがpropsを受け取ってjsxを返すのとは違い、コンポーネントを受け取ってコンポーネントを返す関数のことを指す。

基本的には以下の条件を満たす関数をHOCと呼ぶ。

  1. 副作用の無い純粋な関数である。
  2. 引数(の一つ)としてコンポーネントを受け取る。
  3. 引数で受け取ったコンポーネントをラップした(内包した)新しいコンポーネントを返す。

利用例

  1. react-reduxのconnect関数
  2. Next.jsのwithRouter関数
  3. 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とかは正確に言うと

connect.js
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で子要素として表現できるため、

render-props2.js
<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(
  <AutoSizer>
    {({ height, width }) => (
      <List
        height={height}
        rowCount={list.length}
        rowHeight={20}
        rowRenderer={rowRenderer}
        width={width}
      />
    )}
  </AutoSizer>,
  document.getElementById('example')
);


Reactのドキュメント読もう。


3. Compound Component


参考

https://kentcdodds.com/blog/advanced-react-component-patterns
コンポーネントのデザインパターン色々

  • Compound Components
  • Higher Order Components
  • Render Props
  • Prop Collections and Getters
  • State Initializers
  • Controlled Components
  • Provider

後ほど色々見てこう。

31
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
29