Skip to content

State管理ライブラリとの連携

公開日:December 8, 2024更新日:December 8, 2024
ReactTypeScriptCoding📄

Reactアプリケーションにおいて、状態管理はアプリの規模や複雑性に応じて適切なアプローチを選択する必要があります。本章では、代表的な状態管理の選択肢と、型安全性を活用した設計、およびサーバーサイドとのデータ連携について解説します。

1. Context API vs Redux vs Zustand などの選択肢

状態管理には多くの選択肢があります。それぞれの特徴を以下にまとめます。

Context API

  • 特徴: React標準の状態管理手段。
  • 利点:
    • 外部ライブラリが不要。
    • シンプルなデータ共有に最適。
  • 欠点:
    • 複雑な状態管理には不向き。
    • 再レンダリングのパフォーマンス問題が発生しやすい。

Redux

  • 特徴: アプリ全体で状態を一元管理。
  • 利点:
    • 拡張性とプラグインエコシステムの充実。
    • 状態の追跡やデバッグが容易 (Redux DevTools)。
  • 欠点:
    • 初期設定が複雑。
    • 設定作業が冗長になりがち。

Zustand

  • 特徴: 軽量かつ直感的な状態管理ライブラリ。
  • 利点:
    • シンプルでボイラープレートが少ない。
    • Context APIの欠点を回避。
  • 欠点:
    • 大規模アプリケーションには向かない場合がある。

どれを選ぶべきか?

  • 小規模なアプリや局所的な状態管理 → Context API
  • 複雑なビジネスロジックや大規模アプリ → Redux
  • シンプルで軽量な状態管理を優先 → Zustand

2. 型安全な状態管理の設計 (TypeScriptによる型付け)

状態管理において型安全性を確保することで、保守性が向上しバグのリスクを軽減できます。以下はTypeScriptを使用した状態管理の例です。

Context APIの型付け

tsx
import React, { createContext, useContext, useState } from "react";

interface AppState {
  user: string;
  isLoggedIn: boolean;
}

interface AppContextProps {
  state: AppState;
  setState: React.Dispatch<React.SetStateAction<AppState>>;
}

const AppContext = createContext<AppContextProps | undefined>(undefined);

export const AppProvider: React.FC = ({ children }) => {
  const [state, setState] = useState<AppState>({ user: "", isLoggedIn: false });

  return (
    <AppContext.Provider value={{ state, setState }}>
      {children}
    </AppContext.Provider>
  );
};

export const useAppContext = () => {
  const context = useContext(AppContext);
  if (!context) throw new Error("useAppContext must be used within AppProvider");
  return context;
};

Redux Toolkitの型付け

tsx
import { configureStore, createSlice, PayloadAction } from "@reduxjs/toolkit";

interface AppState {
  user: string;
  isLoggedIn: boolean;
}

const initialState: AppState = { user: "", isLoggedIn: false };

const appSlice = createSlice({
  name: "app",
  initialState,
  reducers: {
    login: (state, action: PayloadAction<string>) => {
      state.user = action.payload;
      state.isLoggedIn = true;
    },
    logout: (state) => {
      state.user = "";
      state.isLoggedIn = false;
    },
  },
});

export const { login, logout } = appSlice.actions;

export const store = configureStore({ reducer: appSlice.reducer });

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

3. 複雑な状態を扱う際のベストプラクティス

  1. 状態のスコープを限定する:

    • 状態は必要なコンポーネントにのみ提供する。
    • グローバル状態とローカル状態を使い分ける。
  2. 不変性を維持する:

    • 状態を直接変更せず、setStatedispatchを使用する。
  3. メモ化を活用する:

    • useMemouseCallbackを使用して不要な再レンダリングを防止。
  4. 型安全性を重視する:

    • TypeScriptの型チェックで意図しないバグを防ぐ。
  5. 依存関係を最小限に:

    • 必要以上に多くの状態を1つにまとめない。

4. サーバーからのデータ取得と状態管理の統合

React Queryの活用

React Queryは、サーバーからのデータ取得とキャッシュ管理を効率的に行えるライブラリです。

tsx
import { useQuery } from "react-query";

const fetchUser = async (): Promise<User> => {
  const response = await fetch("/api/user");
  if (!response.ok) throw new Error("Network response was not ok");
  return response.json();
};

const UserProfile: React.FC = () => {
  const { data, error, isLoading } = useQuery("user", fetchUser);

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return <div>Welcome, {data.name}!</div>;
};

RTK Queryの活用

Redux Toolkit Query (RTK Query) を使うと、Reduxの状態管理とサーバーサイドデータの統合が容易になります。

tsx
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

interface User {
  id: string;
  name: string;
}

const api = createApi({
  reducerPath: "api",
  baseQuery: fetchBaseQuery({ baseUrl: "/api" }),
  endpoints: (builder) => ({
    getUser: builder.query<User, void>({ query: () => "user" }),
  }),
});

export const { useGetUserQuery } = api;

const UserProfile: React.FC = () => {
  const { data, error, isLoading } = useGetUserQuery();

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return <div>Welcome, {data?.name}!</div>;
};

これらのアプローチを理解し、プロジェクトの規模や特性に応じて選択することで、Reactアプリケーションの状態管理をより効率的に設計できます。