Skip to content

状態管理.mdx

📚 Reactの基礎について: 基本的なReactの概念(useState、useEffect、コンポーネント、Props、Context APIなど)については、Reactガイドを参照してください。このドキュメントでは、Next.jsでの状態管理に焦点を当てます。

📚 Reactの基礎について: 基本的なReactの概念(useState、useEffect、コンポーネント、Propsなど)については、Reactガイドを参照してください。このドキュメントでは、Next.jsでの状態管理に焦点を当てます。

状態管理は、アプリケーションのデータを一元的に管理し、複数のコンポーネント間で共有・更新する仕組みです。これにより、コンポーネントツリーを深く辿ってデータを渡す**「Props Drilling」**の問題が解決され、アプリケーションのデータフローが予測可能になります。

📚 Props Drillingの詳細: Props Drillingの問題とContext APIによる解決方法については、ReactガイドのContext APIを参照してください。

問題のあるコード:

// 問題: Propsを何層にも渡す必要がある
// 注意: useStateの詳細については、Reactガイドを参照してください
function App() {
const [user, setUser] = useState(null);
return <Layout user={user} setUser={setUser} />;
}
function Layout({ user, setUser }: { user: User | null; setUser: (user: User) => void }) {
return <Header user={user} setUser={setUser} />;
}
function Header({ user, setUser }: { user: User | null; setUser: (user: User) => void }) {
return <UserMenu user={user} setUser={setUser} />;
}
function UserMenu({ user, setUser }: { user: User | null; setUser: (user: User) => void }) {
// ようやく使用できる
return <div>{user?.name}</div>;
}
// 問題点:
// - 中間コンポーネントが不要なpropsを受け取る
// - コードが冗長になる
// - 型定義が複雑になる
// - リファクタリングが困難

解決: 状態管理ライブラリの使用

// 解決: Zustandを使用
import { create } from 'zustand';
interface UserStore {
user: User | null;
setUser: (user: User) => void;
}
const useUserStore = create<UserStore>((set) => ({
user: null,
setUser: (user) => set({ user }),
}));
// どのコンポーネントからでも直接アクセス可能
function UserMenu() {
const user = useUserStore((state) => state.user);
return <div>{user?.name}</div>;
}

1. データの単一の真実の源(Single Source of Truth)

📚 useStateの詳細: useStateの詳細については、ReactガイドのuseStateとuseEffectを参照してください。

// 問題: 状態が複数の場所に散在
// 注意: useStateの詳細については、Reactガイドを参照してください
function ComponentA() {
const [cart, setCart] = useState([]);
// ...
}
function ComponentB() {
const [cart, setCart] = useState([]); // 同じ状態が重複
// ...
}
// 解決: グローバルストアで一元管理
const useCartStore = create((set) => ({
items: [],
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
}));
// すべてのコンポーネントが同じ状態を参照
function ComponentA() {
const items = useCartStore((state) => state.items);
// ...
}
function ComponentB() {
const items = useCartStore((state) => state.items); // 同じ状態
// ...
}

2. 予測可能なデータフロー

📚 useEffectの詳細: useEffectの詳細については、ReactガイドのuseStateとuseEffectを参照してください。

// 問題: 状態の更新が予測困難
// 注意: useStateとuseEffectの詳細については、Reactガイドを参照してください
function Component() {
const [data, setData] = useState(null);
useEffect(() => {
// 複数の場所で状態を更新
fetchData1().then(setData);
fetchData2().then(setData); // 競合状態の可能性
}, []);
}
// 解決: 一元化された更新ロジック
const useDataStore = create((set) => ({
data: null,
fetchData: async () => {
const data1 = await fetchData1();
const data2 = await fetchData2();
set({ data: { ...data1, ...data2 } }); // 予測可能な更新
},
}));

3. パフォーマンス最適化

// 問題: 不要な再レンダリング
function ExpensiveComponent({ user }: { user: User }) {
// userが変更されると再レンダリング
return <ExpensiveRender user={user} />;
}
// 解決: 必要な部分だけを購読
function ExpensiveComponent() {
// user.nameだけを購読(userオブジェクト全体の変更には反応しない)
const userName = useUserStore((state) => state.user?.name);
return <ExpensiveRender userName={userName} />;
}

Zustandは、シンプルでミニマリストな状態管理ライブラリです。フックAPIをベースにしており、ボイラープレートが非常に少ないのが特徴です。

  • 特徴: 少ないコードでグローバルストアを作成でき、useStoreフックで必要な状態だけを簡単に取得できます。

Reduxは、大規模で複雑なアプリケーション向けの状態管理ライブラリです。**「単一方向データフロー」**に基づき、厳格なルールでデータの変更を管理します。

  • 特徴: 状態の変更をアクションとリデューサーを通じて行うため、予測可能性が高く、デバッグが容易です。ただし、設定が複雑になりがちですが、Redux Toolkitで簡素化できます。

Recoilは、Reactの思想に近い状態管理ライブラリで、状態を**「Atom」**という小さな単位に分割して管理します。

  • 特徴: useStateに似た感覚で使え、Atom(最小単位の状態)とSelector(Atomから派生した状態)を通じて、必要な状態だけを効率的に購読できます。

Jotai(ジョタイ)は、日本語の「状態」から名付けられたライブラリで、Recoilと同様にアトミックな状態管理を採用しています。

  • 特徴: RecoilよりもさらにミニマリストなAPIを提供し、useStateフックに非常に近い感覚でグローバル状態を扱えます。ボトムアップで小さなAtomを組み合わせて複雑な状態を構築するのに適しています。

どのライブラリを選ぶべきか?

Section titled “どのライブラリを選ぶべきか?”
比較項目ZustandReduxRecoilJotai
学習コスト非常に低い高い低い非常に低い
ボイラープレート非常に少ない多い少ない少ない
規模小〜中規模大規模小〜大規模小〜大規模
コンセプトフックベース単一方向データフローAtom & SelectorAtom & ボトムアップ
  • シンプルさ: ZustandとJotaiは、直感的に使い始めたい場合に最適です。

  • 大規模開発: Reduxは、厳格なルールと予測可能性が求められる大規模なエンタープライズアプリケーションに向いています。

  • Reactとの親和性: RecoilとJotaiは、アトミックなアプローチにより、コンポーネントベースのReact開発と相性が良いです。特にJotaiは、よりミニマルなAPIを好む開発者におすすめです。


状態管理を理解する際、以下の3つの役割を「どこに配置するか」を明確にすることが重要です。それぞれの役割を理解することで、適切な状態管理の選択ができるようになります。

1. Local State(ローカル状態): その場限りの使い捨て

Section titled “1. Local State(ローカル状態): その場限りの使い捨て”

📚 useStateの詳細: useStateの詳細な使い方については、ReactガイドのuseStateとuseEffectを参照してください。

役割: useStateを使用したコンポーネント内での状態管理

特徴:

  • そのコンポーネント内でのみ使用される状態
  • コンポーネントがアンマウントされると、状態も破棄される
  • 最もシンプルで、パフォーマンスへの影響が最小限

使用例:

components/Counter.tsx
// 注意: useStateの詳細については、Reactガイドを参照してください
import { useState } from 'react';
function Counter() {
// Local State: このコンポーネント内でのみ使用
const [count, setCount] = useState(0);
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
</div>
);
}

判断基準:

  • 1つのコンポーネント内でのみ使用される
  • 他のコンポーネントと共有する必要がない
  • Props Drillingが2層以内で済む場合は、Local Stateで十分

2. Context API: 「依存性の注入」。状態そのものより、値を奥深くまで届けるパイプライン

Section titled “2. Context API: 「依存性の注入」。状態そのものより、値を奥深くまで届けるパイプライン”

📚 Context APIの詳細: Context APIの詳細な使い方(作成、提供、消費の3ステップ、注意点など)については、ReactガイドのContext APIを参照してください。

役割: コンポーネントツリー全体で値を共有するための仕組み

特徴:

  • 状態の「伝達」に特化した仕組み
  • 状態を保持するためには、useStateuseReducerが必要
  • Contextの値が更新されると、そのContextを使用している全コンポーネントが再レンダリングされる

使用例:

📚 Context APIの詳細: Context APIの詳細な使い方(作成、提供、消費の3ステップ、注意点など)については、ReactガイドのContext APIを参照してください。

context/ThemeContext.tsx
// 注意: Context API、useState、useContextの詳細については、Reactガイドを参照してください
import { createContext, useContext, useState, ReactNode } from 'react';
type Theme = 'light' | 'dark';
interface ThemeContextType {
theme: Theme;
setTheme: (theme: Theme) => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export function ThemeProvider({ children }: { children: ReactNode }) {
// 状態の保持にはuseStateが必要
const [theme, setTheme] = useState<Theme>('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}

3. Server State(サーバー状態): サーバーの影(キャッシュ)。「今、手元にあるデータは最新か?」を管理する専門職

Section titled “3. Server State(サーバー状態): サーバーの影(キャッシュ)。「今、手元にあるデータは最新か?」を管理する専門職”

役割: サーバーからのデータをキャッシュし、再取得や同期を管理

特徴:

  • APIから取得したデータの管理
  • キャッシュ、再取得、同期が自動的に行われる
  • TanStack QueryやSWRなどの専用ライブラリが必要

使用例:

hooks/useUser.ts
import { useQuery } from '@tanstack/react-query';
function UserProfile({ userId }: { userId: string }) {
// Server State: サーバーからのデータを管理
const { data: user, isLoading, error } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
staleTime: 5 * 60 * 1000, // 5分間は新鮮とみなす
});
if (isLoading) return <div>読み込み中...</div>;
if (error) return <div>エラーが発生しました</div>;
return <div>{user.name}</div>;
}

判断基準:

  • サーバーから取得したデータ
  • キャッシュが必要
  • 再取得が必要
  • 同期が必要

useContextのまさかりポイント:それは「状態管理」ではない

Section titled “useContextのまさかりポイント:それは「状態管理」ではない”

📚 Context APIの詳細: Context APIの詳細な説明については、ReactガイドのContext APIを参照してください。

よくある誤解ですが、Context APIは単体では「状態管理ライブラリ」ではありません

Contextは「値を伝達する仕組み」に過ぎません。 状態の保持には結局useStateuseReducerが必要です。

// ❌ 誤解: Contextが状態を保持している
// 注意: Context APIの詳細については、Reactガイドを参照してください
const ThemeContext = createContext(null); // これは値の伝達の仕組みのみ
// ✅ 正解: useStateが状態を保持している
// 注意: useStateの詳細については、Reactガイドを参照してください
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState('light'); // ここで状態を保持
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}

注意点: Contextの再レンダリング問題

Section titled “注意点: Contextの再レンダリング問題”

📚 Context APIの注意点: Context APIの詳細な注意点(再レンダリングの問題など)については、ReactガイドのContext APIを参照してください。

Contextの値が更新されると、そのContextを使っている(消費している)全コンポーネントが再レンダリングされるため、頻繁に更新されるグローバルな状態(例:タイマーや入力値)には不向きです。

context/TimerContext.tsx
// ❌ バッドプラクティス: 頻繁に更新される状態をContextで管理
// 注意: useStateとuseEffectの詳細については、Reactガイドを参照してください
export function TimerProvider({ children }: { children: ReactNode }) {
const [time, setTime] = useState(new Date());
useEffect(() => {
const interval = setInterval(() => {
setTime(new Date()); // 1秒ごとに更新
}, 1000);
return () => clearInterval(interval);
}, []);
return (
<TimerContext.Provider value={{ time }}>
{children} {/* 問題: 1秒ごとに全コンポーネントが再レンダリングされる */}
</TimerContext.Provider>
);
}
// ✅ ベストプラクティス: 頻繁に更新されない状態のみContextで管理
// context/ThemeContext.tsx
// 注意: useStateの詳細については、Reactガイドを参照してください
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState('light'); // めったに更新されない
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}

「Contextは、認証情報、テーマ、言語設定など、めったに変わらない『静的な共有データ』に限定して使いましょう。」

適切な使用例:

  • ✅ 認証情報(ユーザー情報、ログイン状態)
  • ✅ テーマ(ライト/ダークモード)
  • ✅ 言語設定(i18n)
  • ✅ ルーティング情報

不適切な使用例:

  • ❌ タイマー(頻繁に更新される)
  • ❌ フォームの入力値(頻繁に更新される)
  • ❌ アニメーションの状態(頻繁に更新される)
  • ❌ サーバーから取得したデータ(TanStack QueryやSWRを使用すべき)

これらは「データを取ってくるツール」ではなく、**「サーバーの状態をクライアントに同期させるキャッシュマネージャー」**です。

TanStack Query(旧React Query)の強み(SWRとの比較)

Section titled “TanStack Query(旧React Query)の強み(SWRとの比較)”

TanStack Queryの思想: 「いつ取るか」ではなく「このキー(['user', id])のデータが欲しい」と宣言するだけです。

// ✅ TanStack Query: 宣言的なデータ取得
import { useQuery } from '@tanstack/react-query';
function UserProfile({ userId }: { userId: string }) {
// 「user」と「userId」のキーでデータが欲しいと宣言
const { data: user } = useQuery({
queryKey: ['user', userId], // キャッシュのキー
queryFn: () => fetchUser(userId), // データ取得関数
staleTime: 5 * 60 * 1000, // 5分間は新鮮
});
return <div>{user?.name}</div>;
}
// ✅ SWR: 同様に宣言的
import useSWR from 'swr';
function UserProfile({ userId }: { userId: string }) {
// 「user」と「userId」のキーでデータが欲しいと宣言
const { data: user } = useSWR(`/api/users/${userId}`, fetcher);
return <div>{user?.name}</div>;
}

2. 自動再取得(Window Focus Refetching)

Section titled “2. 自動再取得(Window Focus Refetching)”

ブラウザのタブを切り替えて戻ってきた瞬間に、裏側でデータを最新にする機能です。これはSWRにもありますが、TanStack Queryの方が細かい制御(Mutationの成功後に特定のキーを無効化するなど)に長けています。

// ✅ TanStack Query: 細かい制御が可能
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
function UserProfile({ userId }: { userId: string }) {
const queryClient = useQueryClient();
// ユーザー情報を取得
const { data: user } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
staleTime: 5 * 60 * 1000,
refetchOnWindowFocus: true, // ウィンドウフォーカス時に再取得
});
// ユーザー情報を更新
const updateUser = useMutation({
mutationFn: (data: UserData) => updateUser(userId, data),
onSuccess: () => {
// 更新成功後、特定のキーを無効化(再取得をトリガー)
queryClient.invalidateQueries({ queryKey: ['user', userId] });
// または、関連するすべてのキーを無効化
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
return (
<div>
<div>{user?.name}</div>
<button onClick={() => updateUser.mutate({ name: '新しい名前' })}>
更新
</button>
</div>
);
}

TanStack Queryは、より細かいキャッシュ戦略の制御が可能です。

// ✅ TanStack Query: 詳細なキャッシュ制御
import { useQuery } from '@tanstack/react-query';
function ProductList() {
const { data: products } = useQuery({
queryKey: ['products'],
queryFn: () => fetchProducts(),
staleTime: 5 * 60 * 1000, // 5分間は新鮮とみなす
gcTime: 10 * 60 * 1000, // 10分間キャッシュを保持(旧cacheTime)
refetchOnMount: true, // マウント時に再取得
refetchOnWindowFocus: true, // ウィンドウフォーカス時に再取得
refetchOnReconnect: true, // ネットワーク再接続時に再取得
});
return <div>{/* ... */}</div>;
}
機能TanStack QuerySWR
宣言的なデータ取得
自動再取得
キャッシュ制御✅(詳細)✅(シンプル)
Mutation後の無効化✅(柔軟)✅(限定的)
DevTools✅(優秀)
バンドルサイズ約13KB約4KB
学習コスト
TypeScriptサポート✅(優秀)

TanStack Queryを選ぶべき場合:

  • 複雑なキャッシュ戦略が必要
  • Mutationの制御が重要
  • DevToolsでデバッグしたい
  • 大規模なアプリケーション

SWRを選ぶべき場合:

  • シンプルで軽量な解決策が必要
  • バンドルサイズを最小限に抑えたい
  • 学習コストを低く抑えたい
  • 小〜中規模のアプリケーション

状態管理ライブラリの選択判断

Section titled “状態管理ライブラリの選択判断”

サーバー状態 vs クライアント状態

Section titled “サーバー状態 vs クライアント状態”

重要な区別:

// サーバー状態: APIから取得したデータ
// - キャッシュが必要
// - 再取得が必要
// - 同期が必要
// → React Query / SWR が適している
import { useQuery } from '@tanstack/react-query';
function UserProfile({ userId }: { userId: string }) {
const { data, isLoading, error } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
staleTime: 5 * 60 * 1000, // 5分間は新鮮
cacheTime: 10 * 60 * 1000, // 10分間キャッシュ
});
if (isLoading) return <Loading />;
if (error) return <Error />;
return <div>{data.name}</div>;
}
// クライアント状態: UIの状態
// - モーダルの開閉
// - フォームの入力値
// - グローバルなUI状態
// → Zustand / Jotai / Redux が適している
import { create } from 'zustand';
interface UIStore {
isModalOpen: boolean;
openModal: () => void;
closeModal: () => void;
}
const useUIStore = create<UIStore>((set) => ({
isModalOpen: false,
openModal: () => set({ isModalOpen: true }),
closeModal: () => set({ isModalOpen: false }),
}));

状態管理ライブラリの詳細比較

Section titled “状態管理ライブラリの詳細比較”

Zustandの適用範囲:

// Zustandが適している場合:
// 1. シンプルなグローバル状態
// 2. ミドルウェアが不要
// 3. 学習コストを抑えたい
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
interface CartStore {
items: CartItem[];
addItem: (item: CartItem) => void;
removeItem: (id: string) => void;
}
const useCartStore = create<CartStore>()(
devtools(
persist(
(set) => ({
items: [],
addItem: (item) => set((state) => ({
items: [...state.items, item]
})),
removeItem: (id) => set((state) => ({
items: state.items.filter(item => item.id !== id)
})),
}),
{ name: 'cart-storage' } // localStorageに永続化
)
)
);
// メリット:
// - シンプルで理解しやすい
// - ボイラープレートが少ない
// - TypeScriptとの統合が良い
// デメリット:
// - 複雑な状態遷移には不向き
// - タイムトラベルデバッグがない

Reduxの適用範囲:

// Reduxが適している場合:
// 1. 複雑な状態遷移
// 2. タイムトラベルデバッグが必要
// 3. 厳格な状態管理が必要
// 4. 大規模なエンタープライズアプリケーション
import { createSlice, configureStore } from '@reduxjs/toolkit';
const cartSlice = createSlice({
name: 'cart',
initialState: { items: [] },
reducers: {
addItem: (state, action) => {
state.items.push(action.payload);
},
removeItem: (state, action) => {
state.items = state.items.filter(item => item.id !== action.payload);
},
},
});
export const store = configureStore({
reducer: {
cart: cartSlice.reducer,
},
});
// メリット:
// - 予測可能な状態管理
// - 優秀なデバッグツール
// - ミドルウェアのエコシステム
// デメリット:
// - 学習コストが高い
// - ボイラープレートが多い
// - 小規模プロジェクトには過剰

Jotaiの適用範囲:

// Jotaiが適している場合:
// 1. アトミックな状態管理
// 2. 細かい粒度での最適化が必要
// 3. Reactの思想に近い状態管理
import { atom, useAtom } from 'jotai';
// アトミックな状態
const userAtom = atom<User | null>(null);
const cartAtom = atom<CartItem[]>([]);
// 派生状態
const cartCountAtom = atom((get) => get(cartAtom).length);
function CartButton() {
const [cart] = useAtom(cartAtom);
const count = useAtomValue(cartCountAtom); // カウントだけを購読
return <button>Cart ({count})</button>;
}
// メリット:
// - 非常に細かい粒度での最適化
// - useStateに近い感覚
// - ボトムアップで状態を構築
// デメリット:
// - 複雑な状態遷移には不向き
// - エコシステムが小さい

プロジェクト規模による選択:

// 小規模プロジェクト(< 50コンポーネント)
// → Zustand + React Query
// 理由: シンプル、学習コストが低い、十分な機能
// 中規模プロジェクト(50-200コンポーネント)
// → Zustand + React Query + Context API
// 理由: 機能ごとに状態を分離、グローバル状態はZustand
// 大規模プロジェクト(200+コンポーネント)
// → Redux Toolkit + React Query
// 理由: 厳格な状態管理、タイムトラベルデバッグ、複雑な状態遷移

状態の種類による選択:

// サーバー状態 → React Query
const { data } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
});
// UI状態 → Zustand
const useUIStore = create((set) => ({
isModalOpen: false,
toggleModal: () => set((state) => ({ isModalOpen: !state.isModalOpen })),
}));
// フォーム状態 → React Hook Form(状態管理ライブラリではないが)
const { register, handleSubmit } = useForm();
// グローバルなビジネスロジック → Redux / Zustand
const useOrderStore = create((set) => ({
orders: [],
createOrder: async (orderData) => {
const order = await createOrderAPI(orderData);
set((state) => ({ orders: [...state.orders, order] }));
},
}));

間違い1: すべての状態をグローバルストアに配置

// 悪い例: ローカル状態をグローバルストアに
const useFormStore = create((set) => ({
inputValue: '', // 問題: 1つのコンポーネントでしか使用しない
setInputValue: (value: string) => set({ inputValue: value }),
}));
// 良い例: ローカル状態はuseStateを使用
// 注意: useStateの詳細については、Reactガイドを参照してください
function Form() {
const [inputValue, setInputValue] = useState(''); // ローカル状態
return <input value={inputValue} onChange={(e) => setInputValue(e.target.value)} />;
}

間違い2: サーバー状態をクライアント状態管理ライブラリで管理

// 悪い例: Zustandでサーバー状態を管理
const useUsersStore = create((set) => ({
users: [],
fetchUsers: async () => {
const users = await fetch('/api/users').then(res => res.json());
set({ users });
},
}));
// 問題:
// - キャッシュ機能がない
// - 再取得のロジックが複雑
// - エラーハンドリングが不十分
// 良い例: React Queryでサーバー状態を管理
const { data: users } = useQuery({
queryKey: ['users'],
queryFn: () => fetch('/api/users').then(res => res.json()),
staleTime: 5 * 60 * 1000,
});

間違い3: Props Drillingを過度に避ける

📚 Context APIの詳細: Context APIの詳細な使い方については、ReactガイドのContext APIを参照してください。

// 悪い例: 2-3層のProps Drillingを避けるためにグローバルストアを使用
// 親 → 子 → 孫 の3層だけなのにグローバルストアを使用
// 良い例: Context APIで十分な場合
// 注意: Context APIの詳細については、Reactガイドを参照してください
const UserContext = createContext<User | null>(null);
function App() {
const [user, setUser] = useState<User | null>(null);
return (
<UserContext.Provider value={user}>
<Layout />
</UserContext.Provider>
);
}
// 判断基準:
// - 2-3層のProps Drilling → Context API
// - 4層以上、または複数のブランチ → 状態管理ライブラリ

状態管理の深い理解において重要なポイント:

  1. 状態の種類を理解: サーバー状態 vs クライアント状態
  2. 適切なライブラリの選択: プロジェクト規模と要件に応じて
  3. 状態の配置判断: グローバル vs ローカル vs Context
  4. アンチパターンの回避: 過度なグローバル化を避ける

シニアエンジニアとして考慮すべき点:

  • 状態のスコープ: 状態が必要な範囲を正確に判断
  • パフォーマンス: 不要な再レンダリングを避ける
  • 保守性: 長期的に保守可能な状態管理設計
  • チームの合意: チーム全体で統一された状態管理戦略