React のHOC(高次コンポーネント)の使い所 2: Inheritance Inversion


前回の記事 では HOC ( High Order Component: 高次コンポーネント)について Props Proxy という手法で実装する方法について触れました。

この記事では、Inheritance Inversion (継承の逆転)という手法での実装を見ていきます。

Inheritance Inversion による実装

前回の記事で触れたかんばんアプリを Inheritance Inversion で実装してみます。

動くコードはこちら

Props Proxy では WrappedComponent を文字通りラップすることで HOC を実装しました。

Inheritance Inversion では、WrappedComponent継承します

ソースコードを見るとわかるように、 ラップする側の HOC ファクトリメソッドが WrappedComponent から継承されるのではなく、逆に WrappedComponent を継承するため “Inheritance Inversion”(逆転した継承)と呼ばれます。

これにより WrappedComponent がもつ

  • state
  • props
  • ライフサイクルメソッド1componentDidMount など)
  • render

といったメソッドやメンバ変数へ直接アクセスできるようになります。

Props Proxy との違い

Props Proxy はラップするコンポーネント WrappedComponentrender メソッド内でそのまま利用していました。つまり、HOC は単純なラッパーとして機能していました。

対して Inheritance Inversion は WrappedComponent を継承することで、コンポーネントのクラス定義自体を拡張・改変することができます。

ユースケース

この違いにより Inheritance Inversion では、Props Proxy とは違った柔軟な実装ができます。

render の処理を乗っ取る

例えば、 render メソッドの呼び出しをジャック2して、 props へ渡されたデータ配列が空の場合に代替テキストを表示させるといったことができます。

props の書き換え

props を動的に追加・更新することができます。

この例では WrappedComponentinput 要素が渡されたとき、name 属性に任意のプレフィックスを付与しています。

注意点として、継承元のオブジェクト(super から取得できるオブジェクト)を直接編集してはいけません3。これを避けるため、

  • props はスプレッド構文4を利用し、新たなオブジェクトを生成して利用する(wrappedTree.props を複製し、newProps のオブジェクトとマージしている)
  • render メソッドで取得したコンポーネントツリーを cloneElement で複製する

といったことを行っています。

スプレッド構文によるオブジェクトの複製(下記)は、

次のように従来の方法で行ってももちろん問題ありません。

Inheritance Inversion を利用する上での注意

Inheritance Inversion による実装は、Props Proxy と比べクラスメソッドや state などのメンバ変数に直接アクセスできる分、より柔軟な実装が可能です。 ただそれだけ、ラップするコンポーネントの破壊的操作を可能とします。

  • 制御対象でない props を改変しない
  • 継承元の render メソッドだけでなく、必要なライフサイクルメソッドも呼び出す

といったことに気を遣い、シンプルな実装を心がけましょう。

HOC まとめ

2 つの記事に分けて React の HOC (High Order Component: 高次コンポーネント)と呼ばれる手法について紹介しました。

HOC はコンポーネントを受け取りコンポーネントを返却する関数で、コードの再利用性を高めることができます。

また、2 つの実装手法がありました。

  • Props Proxy では コンポーネントのラッパーを生成することで、コンポーネントへ props の追加やスタイリングなどを行うことができる
  • Inheritance Inversion ではコンポーネントのクラスを継承し、各種クラスメソッドやメンバ変数を直接制御できる

HOC を利用する場合、基本は単純なラッパーである Props Proxy で実装できないか考えるべきです。 Inheritance Inversion はより破壊的操作が可能なため、適切なケースを見極めて利用しましょう。


CodePen のサンプルはこちらにまとめています。


  1. State and Lifecycle

  2. Render Hijacking (レンダリングハイジャック)と呼ばれます

  3. React のライフサイクル内で値の変更を検知できず、予期しない挙動を引き起こすことがあります

  4. スプレッド構文 – JavaScript | MDN