Closed8

HOCとRender PropsとHooksの使い分け

kazizikazizi

前提

HOC

React のコンポジションの性質から生まれる設計パターン。
横断的関心事を処理。

ラップされたコンポーネントはコンテナの props のすべてに加えて新規のプロパティである data を受け取り、出力の描画に使用します。外側にある HOC は渡すデータが使われる方法や理由には関心がありませんし、ラップされたコンポーネントの側はデータがどこからやって来たのかには関心を持ちません

規則

  • 自身に関係のない props はラップされるコンポーネントにそのまま渡す
  • 組み立てやすさを最大限保つ
  • デバッグしやすくするため表示名をラップする

https://ja.reactjs.org/docs/higher-order-components.html

https://qiita.com/putan/items/f38bf50422efa1a0b2cb

Render Props

あるコンポーネントが何をレンダーすべきかを知るために使う関数型の props。
横断的関心事を処理。
多くの高階コンポーネント (HOC) がレンダープロップを使った通常のコンポーネントによって実装可能。

https://ja.reactjs.org/docs/render-props.html

https://zenn.dev/morinokami/books/learning-patterns-1/viewer/render-props-pattern

Hooks

フックを使えば、ステートを持ったロジックを、コンポーネントの階層構造を変えることなしに再利用できる

React をしばらく使った事があれば、この問題を解決するためのレンダープロップや高階コンポーネントといったパターンをご存じかもしれません。しかしこれらのパターンを使おうとするとコンポーネントの再構成が必要であり、面倒なうえにコードを追うのが難しくなります。典型的な React アプリを React DevTools で見てみると、おそらくプロバイダやらコンシューマやら高階コンポーネントやらレンダープロップやら、その他諸々の抽象化が多層に積み重なった『ラッパー地獄』を見ることになるでしょう

https://ja.reactjs.org/docs/hooks-intro.html

kazizikazizi

それぞれ得意なことは何なんだろう。
HooksはRender PropsやHOCなどによるラッパー地獄を回避するための代物らしいが、、🤔

kazizikazizi

参考記事1

Comparison: HOCs vs Render Props vs Hooks

概要

  • 以下の観点から総合的に判断してrender propsが一押し。僅差でhooks。hocは使わないほうが良い。
    • 可読性
    • 再利用のしやすさ
    • カスタムのしやすさ
    • デバッグのしやすさ
    • 単体テストのしやすさ
    • パフォーマンス
  • いつHOCを使うか
    • かなり抽象化してしまうのでほとんど使わないが、ごくシンプルな機能を既存のコンポーネントかサードパーティ製のコンポーネントに適用する場合には使っても良いかも
    • できる人間だと思われたくて同僚に嫌われたいなら使えばいいと思う(皮肉)
  • いつRender Propsを使うか
    • レンダリングを完全に制御したいとき
    • レンダリングとロジックを簡単に分離したいとき
    • 読みやすいコードを書きたいとき
    • クラスコンポーネントを書きたいとき
  • いつHooksを使うか
    • レンダリングとロジックを分離したいとき
    • Render Propsのネストをたくさんする冗長な書き方を避けたいとき
    • 読みやすく再利用しやすいコードを書きたいとき
  • 3つはメリデメがあるので、組み合わせて使うのがベスト
kazizikazizi

参考記事2

React: Hooks vs. Render Props vs. Higher-Order Components

概要

  • HOC, Render Props, Hooksのどれかを使うか決める際は、可能ならどんな状況でもHooksを選ぶべき
  • HOCの何がだめなのか
    • propsの名前がリネームできないので、他で定義されている同名のpropsを上書きする恐れがある
// HOC
<MyComponent x="some value" y="some other value" />

// ^をHOCでラップする際にwithPageにxまたはyが含まれていたらMyComponentのxまたはyが上書きされる
export default withPage(MyComponent)
  • HOCにpropsで何が渡されるかというのが呼び出し元からだとわからず、可読性に欠ける
  • Render Propsの何がだめなのか
    • Render Propsのreturn内でしかデータが使えない
<Mouse>
  {({ x, y }) => (
    <Page>
      {({ x: pageX, y: pageY }) => {
        // ^ big brain
      }}
    </Page>
  )}
</Mouse>
  • 容易にネストするので可読性が落ちてしまう
// 3つRender Propsを使うだけでめちゃネストする
const MyComponent = () => {
  return (
    <Mouse>
      {({ x, y }) => (
        <Page>
          {({ x: pageX, y: pageY }) => (
            <Connection>
              {({ api }) => {
                // yikes
              }}
            </Connection>
          )}
        </Page>
      )}
    </Mouse>
  )
};
  • Hooksの何がいいか
    • propsの名前をリネームできる
const { x, y } = useMouse();
const { x: pageX, y: pageY } = usePage();
  • 何がpropsとして渡されるかがわかりやすい
  • return外でもデータが使える
const { x: pageX, y: pageY } = usePage();

useEffect(() => {
  // this runs whenever pageX or pageY changes
}, [pageX, pageY]);
  • 全然ネストしない
const { x, y } = useMouse();
const { x: pageX, y: pageY } = usePage();
const { api } = useConnection();
kazizikazizi

参考記事3

The Current State of React HoCs, Hooks, and Render Props

概要

  • HOC, Render Props, Hooksにはそれぞれ使い所があり、それぞれの完全な代替にはなりえない。Hooksがメジャーになった今でも、HOCやRender Propsを使ったほうが良いケースはいくつかある
  • 例えばテストのしやすさで言えばHOCに分がある
    • Reduxのconnect()を例に考える
// MyComponentをラップしたHOCを返す
connect()(MyComponent);
  • ReduxではuseSelectorとuseDispatchを使えば、connect()を使わなくても同じことを実現できる
  • が、HOCは全てのdataをprops経由で受け取るのでテストはしやすい
kazizikazizi

参考記事4

Thoughts on React Hooks, Redux, and Separation of Concerns

概要

  • Hooksに飛びついて銀の弾丸の如く扱うのは間違い
  • HooksにもHOCにもトレードオフがあるのでそれを認識して取り扱っていくべき
    • HOC
      • メリット
        • すべてのデータをprops経由で受け取るのでシンプルなコンポーネントになりやすい
        • 関心の分離がしやすい
      • デメリット
    • Hooks
      • メリット
        • componentのネストを抑えられる
        • シンプルな関数としてロジックを切り出しやすい
        • 静的型付けがしやすい
        • 構成しやすい
      • デメリット
        • 関心の分離は実現しにくくなってしまう
          • props経由以外でもデータを受け取れるし、stateを定義したりできるので、扱う関心事が複数になりやすい
kazizikazizi

参考記事5

Render Props vs React Hooks

概要

  • Render Propsは死んでない = Hooksが出てきた今でも有用性はある
  • Hooksを使うと再レンダリングが容易に発生しパフォーマンスが低下するリスクがある
    • 以下の例だとinputに入力されるたびにHeaderやNavigationなどの子コンポーネントも合わせて再レンダリングされてしまう
function Page() {
  // react-hook-formとは関係ないcustom hook
  const { values, setValue } = useForm();

  return (
    <>
      <Header />
      <Navigation />
      <SomeOtherThirdPartyComponent />
      <form>
        <input
          value={values.name}
          onChange={e => {
            setValue("name", e.target.value);
          }}
        />
        <input
          value={values.email}
          onChange={e => {
            setValue("email", e.target.value);
          }}
        />
      </form>
      <Footer />
    </>
  );
}
  • Render Propsを使えば特定の箇所だけ再レンダリングすることが可能
    • 以下の例だとFormManagerコンポーネント内だけ再レンダリングされる
function Page() {
  return (
    <>
      <Header />
      <Navigation />
      <SomeOtherThirdPartyComponent />
      <FormManager>
        {({ values, setValue }) => (
          <form>
            <input
              value={values.name}
              onChange={e => {
                setValue("name", e.target.value);
              }}
            />
            <input
              value={values.email}
              onChange={e => {
                setValue("email", e.target.value);
              }}
            />
          </form>
        )}
      </FormManager>
      <Footer />
    </>
  );
}
  • 再レンダリングをより制御したい場合はRender Propsを使うべき
kazizikazizi

所感

  • 基本的にHooksでよさそう
    • 以下のデメリットも把握しておけば対処できそう
      • 関心の分離がしづらい、内容が肥大化しがち
        • 適宜componentを切り分ける
      • 再レンダリングを制御しきれない
        • ユースケースに応じてreact-hook-formとかで非制御コンポーネントを活用
  • HOCについてはprops管理がしづらかったりして導入するメリットはそこまでないが以下の全てに当てはまる場合は使ってもいいかもしれない
    • ログ出力などの横断的関心事を扱う場合
    • HOCのネストは避ける
    • チーム開発の場合は、携わるメンバーがある程度HOCの知識・理解がある
  • Render Propsは再レンダリングを完全にコントロールしたい & 非制御コンポーネントは使いたくない場合に使うと良いかも
このスクラップは2023/10/20にクローズされました