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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- Contextを使った複合コンポーネント
- Render PropsとHOCパターン
- モーダルとツールチップのためのPortal
- エラーバウンダリー