UIロジックをグローバルステートに逃がしすぎていないか
はじめに
ReduxやZustand、Recoilなどの状態管理ツールを使っていると、つい「状態は全部そこに集約しておけばいい」と思ってしまいがちです。
しかし実務では、UIの一時的なロジックや表示制御までもグローバルステートに逃がしてしまい、逆に構造が複雑化しているケースがよく見受けられます。
この記事では、レビューアーとして「それ、グローバルで持つ必要ある?」という問いを立てながら、構造の整理と提案の観点を解説します。
グローバルステートに逃げすぎた例
store.ts
export const useGlobalStore = create((set) => ({
user: null,
sidebarOpen: false,
modalVisible: false,
selectedTab: 0,
toast: null,
// ...
}));
UIコンポーネント側はこうなっています:
Sidebar.tsx
const { sidebarOpen, setSidebarOpen } = useGlobalStore();
return (
<aside className={sidebarOpen ? 'open' : ''}>
<button onClick={() => setSidebarOpen(false)}>閉じる</button>
</aside>
);
@ReviewersidebarOpenのようなUI制御フラグは、グローバルで持つよりローカルで管理した方が構造の分離が明確になります。
- 状態がアプリ全体に共有されていることで依存が不要に波及
- コンポーネント単位での状態把握がしづらくなる
- 構造的に「なぜその状態が必要なのか」が不透明になる
レビュー時にチェックすべきポイント
観点 | チェック内容 | 検討すべき対応 |
---|---|---|
状態が画面全体に関与しているか? | 複数コンポーネントで使われているか? | グローバルに置くべき |
状態の寿命は短期的か? | 1画面内で完結するか? | ローカルに持たせる |
状態の意図がUI制御に限定されていないか? | 表示/非表示、開閉など | useStateや局所Contextで代替可 |
他の状態との依存があるか? | 複数ロジックに依存するなら | reducer化または独立管理を検討 |
ローカルに戻した例
Sidebar.tsx
export function Sidebar() {
const [open, setOpen] = useState(false);
return (
<aside className={open ? 'open' : ''}>
<button onClick={() => setOpen(false)}>閉じる</button>
</aside>
);
}
このように戻すことで:
- 構造的な局所性が明確になる
- 他の機能との依存が減る
- 状態の意図がその場で理解できる
グローバルで持つべき状態の例
逆に、以下のような状態はグローバルで持つべきです。
- ログインユーザー情報(
user
) - アプリ全体のトースト通知(
toast
) - タブや言語など、複数画面にまたがるUIの共通制御
その上で、UIフラグとの混在を避けることが重要です。
状態構造を可視化
後者の構造の方が、状態の責務とスコープが明確になっており、保守性が高い設計です。
コメントテンプレート(レビュー用)
グローバル化の指摘
@Reviewer: sidebarOpenのようなUI局所制御の状態がグローバルに置かれており、構造的に不要な依存が発生しています。useStateでローカルに戻すことで、責務が明確になります。
改善提案例
- const { sidebarOpen, setSidebarOpen } = useGlobalStore();
+ const [open, setOpen] = useState(false);
まとめ
状態管理ライブラリは強力ですが、「何でも集めればよい」という構造は逆効果になることがあります。
レビューアーとしては、状態が本当に「アプリ全体で共有されるべきものか?」を見極め、
構造の粒度・寿命・責務という観点で、ローカル管理かグローバル管理かを判断していくことが大切です。
構造がシンプルで意図が明確なコードこそ、チーム全体で読みやすく、安全に育てていけるReactアプリケーションの土台になります。