状態を持ちすぎるコンポーネントの再構成
はじめに
「とりあえずここで持っとくか」と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と関係あるか?」
構造的に“持ちすぎ”を見抜く視点を持つことで、コード全体の構成力や読みやすさを引き上げていくことができます。