0Pricing
Frontend Academy · レッスン

Render PropsとHOCパターン

render関数をpropsとして渡してUIを注入し、高階コンポーネントでコンポーネントをラップして、元のコンポーネントを変更せずに振る舞いを注入します。

「Render PropsとHOCパターン」はCoddyKit上の無料Frontend Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはFrontend Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Frontend Academyコースには全4レッスンが含まれています。

2つの古典的な再利用パターン

フックが登場する前(React 16.8以前)は、状態を持つロジックを再利用する方法として、高階コンポーネント(HOC)またはrender propsのいずれかを使っていました。新しいコードではほとんど使いませんが、既存のコードベースや多くのライブラリで今でも目にします。

Render Props――考え方

コンポーネントがpropsとして関数(一般的にはrenderまたはchildrenという名前)を受け取り、内部状態を引数として呼び出します。その状態を使って何を描画するかは利用者が決めます。

function Mouse({ render }) {
  const [pos, setPos] = useState({ x: 0, y: 0 });
  useEffect(() => {
    const handler = (e) => setPos({ x: e.clientX, y: e.clientY });
    window.addEventListener('mousemove', handler);
    return () => window.removeEventListener('mousemove', handler);
  }, []);
  return render(pos);
}

// Usage:
<Mouse render={({ x, y }) => <p>{x}, {y}</p>} />

関数としてのchildren

childrenプロパティを関数にすることもできます。同じパターンですが、JSXがよりすっきりします。

function Mouse({ children }) {
  const [pos, setPos] = useState({ x: 0, y: 0 });
  /* ... */
  return children(pos);
}

<Mouse>
  {({ x, y }) => <p>{x}, {y}</p>}
</Mouse>

高階コンポーネント――考え方

HOCは、コンポーネントを受け取り、追加のpropsや振る舞いを持つ新しいコンポーネントを返す関数です。Reduxのconnect()やReact RouterのwithRouter()で盛んに使われました。

function withMouse(Component) {
  return function WithMouse(props) {
    const [pos, setPos] = useState({ x: 0, y: 0 });
    useEffect(() => {
      const handler = (e) => setPos({ x: e.clientX, y: e.clientY });
      window.addEventListener('mousemove', handler);
      return () => window.removeEventListener('mousemove', handler);
    }, []);
    return <Component {...props} mouse={pos} />;
  };
}

// Usage:
const Position = ({ mouse }) => <p>{mouse.x}, {mouse.y}</p>;
const EnhancedPosition = withMouse(Position);
<EnhancedPosition />

HOCはラップして合成する

HOCは積み重ねられますが、デバッグが難しい「ラッパー地獄」になります。

// Multiple HOCs:
const Enhanced = withRouter(connect(mapState)(withTranslation()(MyComponent)));
// Equivalent in modern React:
// const Enhanced = MyComponent (reads from hooks: useRouter, useSelector, useTranslation)

Render PropsとHOCの比較

Render propsは、より明示的で型に対応しやすく、名前の衝突もありません。HOCはより宣言的ですが、注意しないとラップしたコンポーネントの静的な型情報が失われ、props名が衝突する可能性もあります。

フックが両方に取って代わった理由

カスタムフックは、より少ない定型コードで同じ目的を実現します。ラッパーコンポーネントも、入れ子になった関数の引数も、複雑な型操作も必要ありません。フックが2025年時点での慣用的な答えです。

// Custom hook equivalent of useMouse:
function useMouse() {
  const [pos, setPos] = useState({ x: 0, y: 0 });
  useEffect(() => {
    const handler = (e) => setPos({ x: e.clientX, y: e.clientY });
    window.addEventListener('mousemove', handler);
    return () => window.removeEventListener('mousemove', handler);
  }, []);
  return pos;
}

// Usage:
const { x, y } = useMouse();

今でもRender Propsが適している場合

状態だけでなくマークアップも注入する必要があるコンポーネントでは、Render propsが今でも自然な選択肢です。たとえば、特定の位置にアイテムを描画する仮想化リストなどです。

function VirtualList({ items, itemHeight, renderItem }) {
  const [scrollTop, setScrollTop] = useState(0);
  const start = Math.floor(scrollTop / itemHeight);
  const visible = items.slice(start, start + 20);
  return visible.map((item, i) => renderItem(item, start + i));
}

<VirtualList items={data} itemHeight={40} renderItem={(item, i) => (
  <Row key={i} data={item} />
)} />

HOCのベストプラクティス(必要な場合)

HOCを記述する必要がある場合は、静的メソッドをコピーし(hoist-non-react-statics)、refを転送し(React.forwardRef)、DevToolsで表示するdisplayNameを維持してください。

HOCでのrefの転送

HOCはデフォルトでrefを隠します。利用者が基になるコンポーネントのrefにアクセスできるよう、forwardRefを使用してください。

function withLogger(Component) {
  const Wrapped = React.forwardRef((props, ref) => {
    console.log('rendering', Component.name);
    return <Component {...props} ref={ref} />;
  });
  Wrapped.displayName = `withLogger(${Component.name})`;
  return Wrapped;
}

レガシーパターンを見分ける

HOCやRender propsは、react-redux(古いAPI)、Apollo Client v2、react-router v5(withRouter)、Formik(Field)、styled-components(withTheme)で目にします。これらのライブラリで新しいAPIが利用できる場合は、フックにリファクタリングしてください。

クイックチェック

Reactコンポーネント間で状態を持つロジックを共有するために使われていた、フックが大部分に取って代わったパターンは何ですか?

復習:Render PropsとHOC

Render propsは、JSXを返す関数をpropsとして渡す方法で、明示的で型に対応しやすい特徴があります。HOCはコンポーネントをラップする関数で、古いライブラリ(connect、withRouter)で使われています。状態を扱うロジックについては、カスタムフックが両方に取って代わりました。カスタムマークアップを注入する必要がある場合(仮想化リストなど)は、今でもRender propsが適しています。HOCを記述するときは、forwardRef、静的メソッドの引き継ぎ、displayNameの設定を行ってください。

よくある質問

「Render PropsとHOCパターン」レッスンは無料ですか?

はい。「Render PropsとHOCパターン」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Frontend Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Frontend Academyコースには全4レッスンが含まれています。

「Render PropsとHOCパターン」で何を学びますか?

render関数をpropsとして渡してUIを注入し、高階コンポーネントでコンポーネントをラップして、元のコンポーネントを変更せずに振る舞いを注入します。 ブラウザで直接実行するハンズオンコードでFrontend Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Frontend Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのFrontend Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「Render PropsとHOCパターン」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このFrontend Academyレッスンでコードを書いて実行できますか?

はい。すべてのFrontend Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Contextを使った複合コンポーネント
  2. Render PropsとHOCパターン
  3. モーダルとツールチップのためのPortal
  4. エラーバウンダリー
← Frontend Academyに戻る