状態を持ちすぎるコンポーネントの再構成
はじめに
「とりあえずここで持っとくか」とuseStateを生やしていった結果、1コンポーネントで7つの状態管理。
これ、実務ではよくある構造ですが、レビューアー視点では“持ちすぎ構造”を見逃さないようにしたいところです。
この記事では、「状態を持ちすぎているコンポーネント」をレビューでどう見抜き、どう再設計を促すべきかを整理します。
状態を持ちすぎている兆候
以下のようなコードに出会ったら、少し注意して読み込むべきです。
状態が多すぎる例
export function ProfilePage() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [phone, setPhone] = useState('');
  const [errors, setErrors] = useState({});
  const [loading, setLoading] = useState(false);
  const [editable, setEditable] = useState(true);
  const [history, setHistory] = useState([]);
@Reviewer状態が7つ以上あり、ロジックも混在しています。グルーピング・構造分割・責務整理を検討しましょう。  return <Form /* ... */ />;
}なぜ「持ちすぎ」は問題になるのか?
- 
認知コストの増加
状態同士の依存関係が増えると、何が何のための状態か読み解きにくくなります。 - 
責務の分離が難しくなる
UIとロジックが混在していると、再利用やテストの妨げになります。 - 
パフォーマンス悪化の原因にもなる
状態の1つが変わるだけで、全部再描画される可能性がある。 
状態整理の観点(レビュー時に見るべき)
| 観点 | チェックするポイント | 提案すべき方向 | 
|---|---|---|
| 状態の数 | 5〜7個以上に増えている | 状態をグルーピング or 別Hookへ分離 | 
| 状態の種類 | UI制御, 入力値, 通信管理が混在 | 責務ごとに切り出し | 
| 更新の関連性 | ある状態が他を更新トリガにしている | reducer構造へ整理 | 
| propsとの重複 | 親から渡されたpropsを再状態化している | そのまま使うかmemoで変換 | 
改善パターン
パターン1:状態をまとめる
const [form, setForm] = useState({
  name: '',
  email: '',
  phone: '',
});const handleChange = (field: keyof typeof form, value: string) =>
  setForm(prev => ({ ...prev, [field]: value }));- 状態がまとまっているのでロジックの記述も簡潔
 useCallbackで安定化しやすい
パターン2:カスタムHookに切り出す
useForm.ts
export function useForm() {
  const [form, setForm] = useState({ name: '', email: '' });
  const [errors, setErrors] = useState({});
  const update = (k, v) => setForm(f => ({ ...f, [k]: v }));
  return { form, errors, update };
}ProfileForm.tsx
const { form, errors, update } = useForm();状態とロジックがセットで分離され、コンポーネントは表示に集中できるようになります。
パターン3:reducerでまとめる
状態間の関係が複雑で、1つの状態変更が他にも影響するような場合は、useReducerの出番です。
useProfileReducer.ts
function reducer(state, action) {
  switch (action.type) {
    case 'update':
      return { ...state, [action.key]: action.value };
    case 'reset':
      return initialState;
    default:
      return state;
  }
}
const [state, dispatch] = useReducer(reducer, initialState);- テストもしやすく
 - 状態変更の副作用が明示的になりやすい
 
構造の違いを図示
後者の構造は「誰が何を持っているか」が明確になり、レビュー時も読みやすくなります。
コメント例
状態過多の指摘
@Reviewer: 状態が多く、ロジックが混在しているため、読み手にとって構造の意図が伝わりづらくなっています。カスタムHookやグループ化で分離すると可読性が高まります。コメント改善提案
- const [name, setName] = ...
- const [email, setEmail] = ...
+ const [form, setForm] = useState({ name: '', email: '' })
まとめ
状態が多すぎるコンポーネントは、一見整って見えても保守性が非常に低くなります。
レビューアーの視点では、以下のような問いを常に意識しておくと良いでしょう。
- 「この状態、本当にここで持つべきか?」
 - 「この状態、まとめられないか?」
 - 「この責務、UIと関係あるか?」
 
構造的に“持ちすぎ”を見抜く視点を持つことで、コード全体の構成力や読みやすさを引き上げていくことができます。

