コンポーネントから責務を追い出しすぎたカスタムHook構造

Reactの設計において「ロジックの抽出」「責務の分離」は、保守性・再利用性・テスト容易性を高めるための基本方針として推奨されます。しかし、設計の方針としての「分離」が過剰に適用されると、かえってコードの意図やデータの流れが見えづらくなるケースが存在します。

この記事では、責務の抽出が過剰になったカスタムHook設計に着目し、レビュー時に見逃してはならない構造的な問題点と判断基準を整理します。

「追い出しすぎ」の構造に潜むリスク

まずは典型的なケースを見てみましょう。

過剰に分離されたカスタムHook
// useFormLogic.ts
export const useFormLogic = () => {
  const [input, setInput] = useState('');
  const [error, setError] = useState('');

  const validate = useCallback(() => {
    if (input.length < 3) {
      setError('短すぎます');
      return false;
    }
    setError('');
    return true;
  }, [input]);

  return { input, setInput, error, validate };
};
UI側コンポーネント
export const FormComponent = () => {
  const { input, setInput, error, validate } = useFormLogic();

  return (
    <form onSubmit={(e) => { e.preventDefault(); validate(); }}>
      <input value={input} onChange={(e) => setInput(e.target.value)} />
      {error && <p>{error}</p>}
    </form>
  );
};
構造過剰分離への指摘例
@Reviewer: 入力値の状態・バリデーション・エラーメッセージをすべてHook側に追い出していますが、UI要素と密接な責務であるため、コンポーネント側で扱っても構造的な負荷は大きくありません。抽出対象が明確に「再利用可能なロジック」でない場合は、過抽出による読解コストの増加に注意が必要です。
過抽出とは

過抽出(over-extraction)とは、本来コンポーネント内にとどめておくべきローカルな責務やUI密結合ロジックまでを共通化・再利用化の名目で外部に追い出してしまう設計傾向を指します。

過抽出が起きやすい構造パターン

以下は、コンポーネントから責務を過剰に分離した結果、構造が複雑化しやすい代表的な例です。

1. UI密結合の状態まで抽出

  • 表示要素に密接なhover状態
  • ダイアログの開閉状態
  • 一時的な入力補助ステート(例:focus補助や文字数カウント)
過剰な状態抽出例
export const useHoverLogic = () => {
  const [hovered, setHovered] = useState(false);
  return { hovered, setHovered };
};
Comment
@Reviewer: `hovered`は特定の表示要素と密結合しており、再利用前提ではないため、外部Hook化する意義が薄く、かえって責務が分散します。

2. 非再利用ロジックの抽出

たとえば、ある特定画面のフォーム構造のみに依存した検証ロジックを、汎用的なuseValidationとして抽出した場合などです。

Comment
@Reviewer: 複数画面で共有されない、かつユースケース固有の検証処理を`useXxx`として抽出する場合、そのHookが「再利用可能か否か」ではなく「ユースケースに埋め込まれることで構造的意味を持つか」を優先して評価してください。

Hookとコンポーネントの責務境界

以下は、責務を適切に分離している構成(左)と、Hookに過剰に責務が集中している構成(右)の構造比較です。

UML Diagram

責務境界を見極めるレビュー指針

以下は、カスタムHook抽出の妥当性を判断する際のレビュー観点です。

  • 抽出されたHookは他のユースケースでも使用されうるか
  • Hookが表示ロジックやUIに強く依存していないか
  • コンポーネント側の構造が単なるデータ受け取り手になっていないか
  • Hookの構造がテストしやすい形になっているか
  • 状態管理の出力がコンポーネント責務と整合しているか

これらを踏まえ、レビューアーは「Hookが構造を明確にしたか」ではなく「コンポーネントとの分担関係が最適か」という観点で判断を行うべきです。