Use Cases

Jotai with Claude Code

Learn about jotai using Claude Code. Practical tips and code examples included.

Jotaiでボトムアップの状態管理を実現する

Jotaiは「アトム」と呼ばれる最小単位で状態を管理するReact向けライブラリです。Recoilに似たアプローチですが、よりシンプルなAPIとTypeScriptファーストの設計が特徴です。Claude Codeはアトム設計から複雑な派生状態の構築まで的確に生成します。

基本的なアトムの設計

Claude Codeにアトムの構成を依頼しましょう。

> Jotaiでダークモード対応のテーマ管理を作って。
> システム設定の検出、永続化、コンポーネントからの切り替えに対応して。
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
import { atomWithStorage } from "jotai/utils";

type Theme = "light" | "dark" | "system";

// localStorage に永続化されるアトム
const themePreferenceAtom = atomWithStorage<Theme>("theme", "system");

// システムのダークモード設定を検出する読み取り専用アトム
const systemDarkModeAtom = atom<boolean>((get) => {
  if (typeof window === "undefined") return false;
  return window.matchMedia("(prefers-color-scheme: dark)").matches;
});

// 実際に適用されるテーマを計算する派生アトム
const resolvedThemeAtom = atom<"light" | "dark">((get) => {
  const preference = get(themePreferenceAtom);
  if (preference === "system") {
    return get(systemDarkModeAtom) ? "dark" : "light";
  }
  return preference;
});

// カスタムフック
export function useTheme() {
  const [preference, setPreference] = useAtom(themePreferenceAtom);
  const resolvedTheme = useAtomValue(resolvedThemeAtom);

  return {
    preference,
    resolvedTheme,
    setTheme: setPreference,
    isDark: resolvedTheme === "dark",
  };
}

派生アトムで複雑な状態を構成

アトムを組み合わせて複雑な状態を表現するパターンです。

// 基本アトム
const todosAtom = atom<Todo[]>([]);
const filterAtom = atom<"all" | "active" | "completed">("all");
const searchQueryAtom = atom("");

// 派生アトム: フィルタリングされたTodo
const filteredTodosAtom = atom((get) => {
  const todos = get(todosAtom);
  const filter = get(filterAtom);
  const query = get(searchQueryAtom).toLowerCase();

  let result = todos;

  // フィルタ適用
  switch (filter) {
    case "active":
      result = result.filter((t) => !t.completed);
      break;
    case "completed":
      result = result.filter((t) => t.completed);
      break;
  }

  // 検索適用
  if (query) {
    result = result.filter((t) => t.title.toLowerCase().includes(query));
  }

  return result;
});

// 統計情報の派生アトム
const todoStatsAtom = atom((get) => {
  const todos = get(todosAtom);
  return {
    total: todos.length,
    active: todos.filter((t) => !t.completed).length,
    completed: todos.filter((t) => t.completed).length,
    completionRate: todos.length
      ? Math.round(
          (todos.filter((t) => t.completed).length / todos.length) * 100
        )
      : 0,
  };
});

非同期アトムでデータ取得

Jotaiの非同期アトムを使ってサーバーからデータを取得するパターンです。

import { atom } from "jotai";
import { atomWithQuery } from "jotai-tanstack-query";

// 非同期のread-writeアトム
const currentUserAtom = atom<User | null>(null);

const fetchUserAtom = atom(
  (get) => get(currentUserAtom),
  async (_get, set) => {
    const response = await fetch("/api/me");
    const user = await response.json();
    set(currentUserAtom, user);
  }
);

// TanStack Queryとの連携
const userQueryAtom = atomWithQuery((get) => ({
  queryKey: ["user", get(userIdAtom)],
  queryFn: async ({ queryKey: [, userId] }) => {
    const res = await fetch(`/api/users/${userId}`);
    return res.json();
  },
  enabled: !!get(userIdAtom),
}));

アトムファミリーで動的なアトム生成

アイテムごとに独立したアトムを動的に生成するパターンです。

import { atom } from "jotai";
import { atomFamily } from "jotai/utils";

// IDごとに独立したアトムを生成
const todoAtomFamily = atomFamily((id: string) =>
  atom<Todo>({
    id,
    title: "",
    completed: false,
    createdAt: new Date(),
  })
);

// IDリストを管理するアトム
const todoIdsAtom = atom<string[]>([]);

// Usage example
function TodoItem({ id }: { id: string }) {
  const [todo, setTodo] = useAtom(todoAtomFamily(id));

  const toggleComplete = () => {
    setTodo((prev) => ({ ...prev, completed: !prev.completed }));
  };

  return (
    <div>
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={toggleComplete}
      />
      <span>{todo.title}</span>
    </div>
  );
}

function TodoList() {
  const ids = useAtomValue(todoIdsAtom);
  return (
    <div>
      {ids.map((id) => (
        <TodoItem key={id} id={id} />
      ))}
    </div>
  );
}

Zustandとの使い分け

JotaiとZustandはどちらもReactの状態管理ライブラリですが、アプローチが異なります。

  • Jotai - ボトムアップ。小さなアトムを組み合わせて状態を構築。コンポーネントに密結合な状態に向く
  • Zustand - トップダウン。ストア単位で状態を設計。グローバルなビジネスロジックに向く

両者を組み合わせることも可能です。UIの局所的な状態はJotai、アプリ全体の状態はZustandという使い分けが効果的です。

Summary

Jotaiのアトミックなアプローチは、コンポーネント単位での状態管理に優れています。Claude Codeを活用すれば、アトム設計から派生状態の構築、非同期処理まで素早く実装できます。

ストア型の状態管理はZustand状態管理ガイドを、Reactでの開発全般はReact開発を爆速にする方法を参照してください。Jotai公式ドキュメントも併せて確認しましょう。

#Claude Code #Jotai #React #state management #アトミック