Open2

React jotaiについて📝

ピン留めされたアイテム
まさぴょん🐱まさぴょん🐱

Jotaiの基本的な使い方など📝

Jotaiは、React向けの軽量な状態管理ライブラリで、Recoilにインスパイアされつつ、よりシンプルで直感的なAPIを提供します。
Jotaiの基本的な使い方について説明します。

1. Jotaiの基本概念

Jotaiは「アトム(atom)」と呼ばれる最小単位の状態をベースに構築されています。アトムは状態を保持し、コンポーネント間で共有可能な小さなデータ片です。特徴は以下の通り:

  • シンプルさ: Reduxのようなボイラープレートが不要。
  • スケーラブル: 小さなアプリから大規模アプリまで対応可能。
  • ReactらしいAPI: Hooksベースで直感的。
  • 双方向依存: アトム間で依存関係を構築可能。

2. インストール

まず、Jotaiをプロジェクトにインストールします。

npm install jotai
# または
yarn add jotai

3. 基本的な使い方

(1) アトムの作成

アトムは状態の最小単位で、atom関数を使って定義します。

import { atom } from 'jotai';

// シンプルなアトム(プリミティブ値)
const countAtom = atom(0);

// オブジェクトを保持するアトム
const userAtom = atom({ name: 'Taro', age: 20 });

(2) アトムの読み書き

アトムはReactコンポーネント内でuseAtomフックを使って読み書きします。

import { useAtom } from 'jotai';
import { countAtom } from './store';

function Counter() {
  // count: 現在の値, setCount: 更新関数
  const [count, setCount] = useAtom(countAtom);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}
  • useAtomはアトムの値と更新関数を返します。
  • コンポーネント間で同じアトムを共有することで、状態が同期されます。

(3) 読み取り専用・書き込み専用のアトム

状態の読み取りや書き込みを分離したい場合、以下のようなフックを使います。

  • useAtomValue: 値の読み取り専用。
  • useSetAtom: 値の更新専用。
import { useAtomValue, useSetAtom } from 'jotai';

function ReadOnlyComponent() {
  const count = useAtomValue(countAtom); // 値のみ取得
  return <p>Count: {count}</p>;
}

function WriteOnlyComponent() {
  const setCount = useSetAtom(countAtom); // 更新関数のみ取得
  return <button onClick={() => setCount(c => c + 1)}>Increment</button>;
}

4. 派生アトム(Derived Atoms)

派生アトムは、既存のアトムの値をもとに計算された新しいアトムです。依存関係を自動的に管理します。

(1) 読み取り専用の派生アトム

例えば、カウント値を2倍にした値を保持するアトムを作成します。

const doubleCountAtom = atom((get) => get(countAtom) * 2);
  • get: 他のアトムの値を取得する関数。
  • 依存するアトム(countAtom)が更新されると自動的に再計算されます。

使用例:

function DoubleCounter() {
  const doubleCount = useAtomValue(doubleCountAtom);
  return <p>Double Count: {doubleCount}</p>;
}

(2) 読み書き可能な派生アトム

派生アトムに書き込みロジックを追加することもできます。

const doubleCountAtom = atom(
  (get) => get(countAtom) * 2, // 読み取り
  (get, set, newValue) => {
    set(countAtom, newValue / 2); // 書き込み
  }
);

使用例:

function DoubleCounter() {
  const [doubleCount, setDoubleCount] = useAtom(doubleCountAtom);
  return (
    <div>
      <p>Double Count: {doubleCount}</p>
      <button onClick={() => setDoubleCount(doubleCount + 2)}>Increment Double</button>
    </div>
  );
}

5. 非同期処理

Jotaiは非同期処理にも対応しています。非同期アトムを作成することで、APIリクエストなどの結果を管理できます。

非同期アトムの例

const userIdAtom = atom(1);

const userDataAtom = atom(async (get) => {
  const userId = get(userIdAtom);
  const response = await fetch(`https://api.example.com/users/${userId}`);
  return response.json();
});

使用例:

function UserProfile() {
  const userData = useAtomValue(userDataAtom);
  return <p>User: {userData?.name}</p>;
}
  • 非同期アトムはuseAtomValueで使用すると、自動的にPromiseの解決を待機します。
  • SuspenseやError Boundaryと組み合わせることで、ローディングやエラー状態を簡単に扱えます。
function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <UserProfile />
    </Suspense>
  );
}

6. JotaiのProvider

通常、Jotaiはグローバルなアトムを前提としますが、特定のスコープ内でアトムの初期値をカスタマイズしたい場合、Providerを使用します。

import { Provider } from 'jotai';

function App() {
  return (
    <Provider>
      <Counter />
    </Provider>
  );
}

Provider内で初期値をオーバーライドすることも可能:

const countAtom = atom(0);

function CustomProvider() {
  return (
    <Provider initialValues={[[countAtom, 100]]}>
      <Counter />
    </Provider>
  );
}

7. 応用例

(1) フォーム管理

Jotaiはフォームの状態管理にも便利です。

const formAtom = atom({ name: '', email: '' });

function Form() {
  const [form, setForm] = useAtom(formAtom);

  return (
    <div>
      <input
        value={form.name}
        onChange={(e) => setForm({ ...form, name: e.target.value })}
        placeholder="Name"
      />
      <input
        value={form.email}
        onChange={(e) => setForm({ ...form, email: e.target.value })}
        placeholder="Email"
      />
    </div>
  );
}

(2) 複数のアトムの組み合わせ

複数のアトムを組み合わせて複雑なロジックを構築できます。

const firstNameAtom = atom('Taro');
const lastNameAtom = atom('Yamada');
const fullNameAtom = atom((get) => `${get(firstNameAtom)} ${get(lastNameAtom)}`);

function Profile() {
  const fullName = useAtomValue(fullNameAtom);
  return <p>Full Name: {fullName}</p>;
}

8. ベストプラクティス

  • アトムの粒度を小さく保つ: 状態を細かく分割することで、依存関係が明確になり、再レンダリングの最適化がしやすくなります。
  • 派生アトムを活用: 計算ロジックは派生アトムに任せ、コンポーネントをシンプルに保ちましょう。
  • Suspenseを活用: 非同期処理ではSuspenseを使ってローディング状態を明示的に管理。
  • Providerを適切に使用: テストや初期値のカスタマイズが必要な場合にのみProviderを使用。

9. 注意点

  • 再レンダリングの最適化: useAtomは依存するアトムが変更されるとコンポーネントを再レンダリングします。不要な再レンダリングを避けるため、必要に応じてuseAtomValueuseSetAtomを使用。
  • グローバルな状態: アトムはグローバルに共有されるため、意図しない状態の共有に注意。
  • TypeScriptとの併用: JotaiはTypeScriptと相性が良く、型推論が強力。型を明示すると安全性を高められます。
import { atom, useAtom } from 'jotai';

const countAtom = atom<number>(0);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

10. まとめ

Jotaiは、シンプルかつ柔軟な状態管理ライブラリとして、Reactプロジェクトに簡単に組み込めます。以下のようなシーンで特に有効です:

  • 小規模な状態管理をシンプルにしたい。
  • Reduxのような複雑な設定を避けたい。
  • 非同期処理や派生状態を簡単に扱いたい。

基本的なatomuseAtomから始め、必要に応じて派生アトムや非同期処理を追加していくのがおすすめです。
公式ドキュメント(https://jotai.org/)も充実しているので、詳細なAPIやユーティリティはそこを参照してください。