はじめに

「このロジック、HOCでくるんでるけど、本当にそれが適切?」
「カスタムHookにできそうだけど、ロジックと責務の関係性は明確?」

Reactでは共通処理の抽出にHigher-Order Component(HOC)Custom Hookの2手法がよく使われますが、どちらを使うかの判断を誤ると、構造の見通しが悪化し、テストや保守が困難になることがあります。

この記事では、レビューアーの視点で「HOCとHookの使い分け」をどう判断すべきか、実例とともに解説していきます。

そもそもHOCとCustom Hookの違いとは?

HOCとは

HOC(Higher-Order Component)とは、コンポーネントを引数に取り、新しいコンポーネントを返す関数です。
主にUIの構造や振る舞いをラップして再利用可能にするために使われます。

Custom Hookとは

Custom HookはReactの関数型APIであるuseXxxをベースに、共通の状態ロジックや副作用処理をコンポーネントとは別に関数として抽出する手法です。副作用や状態を扱いやすくなり、構造も分かりやすくなります。

判断のポイントは「責務の位置」

比較軸 HOC Custom Hook
責務の主体 UI構造も含むラップ ロジックのみを提供
DOM構造 挿入されることがある 変わらない
可読性 コンポーネント階層が深くなりがち コンポーネントに沿った読みやすさ
テスト性 Mockが必要 状態だけ切り出してテスト可能

レビュー対象になりやすいケース

1. UIとロジックが一体のHOCになっている

withUser.tsx
export const withUser = (Component) => (props) => {
  const [user, setUser] = useState(null);
  useEffect(() => {
    fetch('/api/user').then(res => res.json()).then(setUser);
  }, []);
  return <Component {...props} user={user} />;
@Reviewer
非同期処理がHOCに含まれているため、構造の責務が重くなっています。
UIの都合に応じてHookとして使い分けられる形が望ましいです。
};

→ 改善提案:Custom Hookへの分離

useUser.ts
export function useUser() {
  const [user, setUser] = useState(null);
  useEffect(() => {
    fetch('/api/user').then(res => res.json()).then(setUser);
  }, []);
  return user;
}
Dashboard.tsx
const user = useUser();
return <DashboardView user={user} />;

構造の見通しがよくなり、UIへの依存も減ります。

「UI構造を変えるならHOC」パターン

逆に、次のようなケースではHOCのほうが構造的に適していることもあります。

withErrorBoundary.tsx
export function withErrorBoundary(Component) {
  return function Wrapped(props) {
    return (
      <ErrorBoundary>
        <Component {...props} />
      </ErrorBoundary>
    );
  };
}
  • ErrorBoundary を毎回JSXに書くのは煩雑
  • 階層的に共通ラップした方が意図が明確
  • UIレイヤーの責務(構造の装飾)であるため、HOCが妥当

構造の違いを可視化

UML Diagram
UML Diagram

Hookの方が依存関係が明確で、テストしやすい構造になっているのが分かります。

コメントテンプレート(レビュー用)

責務の指摘
@Reviewer: withUserのようなHOCはUI構造とロジックが混在しており、再利用しづらい構造になります。副作用や状態を扱う部分はuseUserとして切り出すと柔軟性が高まります。
コメント例
- const Enhanced = withUser(MyComponent);
+ const user = useUser();
+ <MyComponent user={user} />

カスタムHookを選ぶべき構造とは?

  • 状態ロジックや非同期処理を複数のUIで再利用したいとき
  • useState/useEffect/useReducerなどを複雑化せず整理したいとき
  • 複雑なロジックを責務別に抽出して見通しを良くしたいとき

HOCを選ぶべき構造とは?

  • UI構造全体を共通ラップしたい(例:ThemeProvider, Layout, ErrorBoundary)
  • 複数のUIを同一の構造で包みたい
  • コンポーネント内部に影響を与えず挙動やデコレーションを差し込みたい

まとめ

HOCとCustom Hookは似て非なる抽象化手段です。
レビューでは次の点を判断基準にすると構造の妥当性が見えてきます:

  • 状態と副作用だけならHookで切り出す
  • UI構造ごと共通化したいならHOCが妥当
  • 責務の混在がないか常に確認する

構造レビューでは「抽象化のしすぎ」も「しなさすぎ」も問題になります。HOCとHook、それぞれの立ち位置と役割を理解して、構造にとって本当に適した抽象化をレビューで導き出していきましょう。