Reactでの効率的なデータ共有:Jotaiの使い方と応用例

Reactアプリケーションの開発では、状態管理が重要な課題の一つです。特に、コンポーネント間でデータを効率的に共有し、管理する方法を見つけることが、開発プロセスの効率化やコードの可読性向上につながります。この記事では、軽量かつシンプルな状態管理ライブラリであるJotaiを活用して、Reactアプリケーションにおけるデータ共有を効率化する方法を解説します。Jotaiは、手軽に導入できるだけでなく、柔軟性とパフォーマンスに優れており、複雑なアプリケーションにも適しています。本記事を通じて、Jotaiの基本から実践的な活用方法までを詳しく理解し、よりスムーズなReact開発を目指しましょう。

目次

Jotaiとは?その特徴と利点


Jotaiは、Reactの状態管理をシンプルかつ柔軟に実現するために設計されたライブラリです。Atomと呼ばれる状態オブジェクトを基本単位とし、それを直接操作することで、直感的で分かりやすい状態管理が可能です。

Jotaiの特徴


Jotaiは、以下の点で他の状態管理ライブラリと一線を画しています:

軽量な設計


Jotaiは非常に軽量で、インストール後すぐに利用可能です。パッケージサイズが小さいため、パフォーマンスへの影響を最小限に抑えます。

シンプルなAPI


Jotaiは直感的なAPIを提供し、学習コストが低いのが特徴です。ReduxやMobXのような設定の複雑さがなく、初心者でも簡単に使い始めることができます。

Reactの自然な拡張


JotaiはReactのHooksを活用しているため、Reactの書き方に馴染みのある開発者にとって非常に扱いやすい設計になっています。

Jotaiの利点


Jotaiを利用することで、以下のような利点を得ることができます:

グローバルな状態管理が容易


Atomを使用することで、アプリケーション全体で共有する状態を簡単に管理できます。

分離された状態管理


状態を独立して管理できるため、アプリケーションのモジュール化を促進し、メンテナンス性を向上させます。

パフォーマンスの向上


状態の変更による再レンダリングが影響を受けるコンポーネントに限定されるため、アプリケーション全体のパフォーマンスが向上します。

Jotaiのこれらの特徴と利点により、複雑なアプリケーションでも状態管理が効率化され、開発者が本質的なロジックに集中できる環境が整います。

Jotaiのインストールと基本設定

JotaiをReactプロジェクトに導入するための手順は非常にシンプルです。以下の手順に従ってインストールと基本設定を行い、すぐに利用できる状態を整えましょう。

Jotaiのインストール

  1. プロジェクトのルートディレクトリで、以下のコマンドを実行してJotaiをインストールします:
npm install jotai

または、yarnを使用している場合:

yarn add jotai
  1. インストールが完了したら、jotaiパッケージをプロジェクト内でインポートします。

Atomの基本設定


Jotaiの状態管理の基本単位はAtomです。Atomは、データや状態を格納するオブジェクトであり、Reactのコンポーネントで共有されます。

以下は、基本的なAtomの作成と使用方法の例です:

Atomの作成


jotaiからatom関数をインポートし、初期値を指定してAtomを作成します:

import { atom } from 'jotai';

const countAtom = atom(0); // 初期値は0

Atomの使用


作成したAtomをReactコンポーネント内で使用するには、useAtomフックを用います:

import React from 'react';
import { useAtom } from 'jotai';
import { countAtom } from './store'; // Atomを定義したファイルからインポート

const Counter = () => {
  const [count, setCount] = useAtom(countAtom);

  return (
    <div>
      <p>現在のカウント: {count}</p>
      <button onClick={() => setCount(count + 1)}>増加</button>
      <button onClick={() => setCount(count - 1)}>減少</button>
    </div>
  );
};

export default Counter;

プロジェクトでの初期設定


Jotaiをプロジェクト全体で利用するために、特別なプロバイダや設定は不要です。useAtomを任意のコンポーネントで直接使用できます。

このように、Jotaiのインストールと基本設定は非常に簡単で、すぐに実践的な状態管理を始めることが可能です。次のセクションでは、Jotaiを使ったコンポーネント間でのデータ共有について解説します。

基本的な使い方:Atomを活用した状態管理

Jotaiの基盤であるAtomは、状態を管理するための単純なオブジェクトです。このセクションでは、Atomを用いて状態管理を行う基本的な使い方について詳しく解説します。

Atomの作成と初期化


Atomはatom関数を用いて作成します。以下のコード例では、カウンターの初期値を設定したAtomを作成します:

import { atom } from 'jotai';

export const countAtom = atom(0); // 初期値は0

ここで定義したcountAtomが状態のベースとなります。

Atomの使用:状態の取得と更新


Reactコンポーネント内でAtomを使用するには、useAtomフックを使用します。このフックを利用することで、状態の読み取りと更新が可能になります。

状態を取得して表示する


以下のコードでは、Atomの状態を取得して画面に表示します:

import React from 'react';
import { useAtom } from 'jotai';
import { countAtom } from './store'; // Atomのインポート

const DisplayCount = () => {
  const [count] = useAtom(countAtom);

  return <p>現在のカウント: {count}</p>;
};

状態を更新する


useAtomフックから取得した状態を変更するには、フックが提供するsetCount関数を使用します:

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

const UpdateCount = () => {
  const [count, setCount] = useAtom(countAtom);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>増加</button>
      <button onClick={() => setCount(count - 1)}>減少</button>
    </div>
  );
};

状態の共有


Atomの大きな特徴は、複数のコンポーネント間で簡単に状態を共有できることです。以下は、状態を共有する例です:

import React from 'react';
import { DisplayCount } from './DisplayCount'; // 表示コンポーネント
import { UpdateCount } from './UpdateCount'; // 更新コンポーネント

const App = () => (
  <div>
    <h1>Jotaiを使った状態管理</h1>
    <DisplayCount />
    <UpdateCount />
  </div>
);

export default App;

この例では、countAtomDisplayCountUpdateCountで共有しています。それぞれが同じ状態を参照するため、どちらかで更新すると即座にもう一方にも反映されます。

Jotaiの基本的な使い方のまとめ

  • Atomを作成atom関数で状態の基本単位を定義する。
  • useAtomフックを利用:状態の取得と更新を行う。
  • 状態の共有:複数のコンポーネントで同じAtomを使用することで、グローバルな状態管理が可能。

このように、Jotaiはシンプルな構造でありながら強力な状態管理を実現します。次のセクションでは、より実践的なコンポーネント間でのデータ共有の方法を詳しく説明します。

コンポーネント間でのデータ共有の実現

Jotaiを使用することで、複数のコンポーネント間で状態を簡単に共有できます。これは、Reactアプリケーションで状態をグローバルに管理する必要がある場合に特に有効です。このセクションでは、実践的なデータ共有の方法を解説します。

Atomを利用した状態の共有


Jotaiでは、Atomを単一のデータソースとして利用することで、複数のコンポーネント間で同じ状態を共有できます。以下に、具体的な実装例を示します。

1. 共通のAtomを定義する


まず、状態を管理するためのAtomを定義します:

import { atom } from 'jotai';

export const sharedAtom = atom("初期値"); // 初期値を設定

2. データを表示するコンポーネントを作成する


次に、このAtomを使用してデータを表示するコンポーネントを作成します:

import React from 'react';
import { useAtom } from 'jotai';
import { sharedAtom } from './store';

const DisplayComponent = () => {
  const [sharedData] = useAtom(sharedAtom);

  return <p>共有データ: {sharedData}</p>;
};

export default DisplayComponent;

3. データを更新するコンポーネントを作成する


同じAtomを利用してデータを更新するコンポーネントを作成します:

import React from 'react';
import { useAtom } from 'jotai';
import { sharedAtom } from './store';

const UpdateComponent = () => {
  const [sharedData, setSharedData] = useAtom(sharedAtom);

  return (
    <div>
      <input
        type="text"
        value={sharedData}
        onChange={(e) => setSharedData(e.target.value)}
      />
      <p>現在の入力値: {sharedData}</p>
    </div>
  );
};

export default UpdateComponent;

アプリケーションでの共有データの活用


最後に、これらのコンポーネントを統合してデータ共有を実現します:

import React from 'react';
import DisplayComponent from './DisplayComponent';
import UpdateComponent from './UpdateComponent';

const App = () => (
  <div>
    <h1>Jotaiを使ったデータ共有</h1>
    <UpdateComponent />
    <DisplayComponent />
  </div>
);

export default App;

結果

  • 入力フィールドの更新UpdateComponentで入力した内容がsharedAtomに保存されます。
  • データの即時反映DisplayComponentで、UpdateComponentの変更がリアルタイムに反映されます。

コンポーネント間のデータ共有のメリット

  1. シンプルな構造:グローバルな状態管理を手軽に実現。
  2. リアルタイム同期:Atomの状態変更が即座に全コンポーネントに反映。
  3. 柔軟性:必要なコンポーネントだけが状態を監視・更新可能。

このように、Jotaiを活用すれば、複雑なアプリケーションでも効率的かつシンプルにコンポーネント間でデータ共有を実現できます。次のセクションでは、さらに洗練されたコードの書き方や高度な活用方法を紹介します。

洗練されたコードの書き方:リードオンリーモードと非同期処理

Jotaiを使いこなすためには、基本的な使い方に加えて、リードオンリーモードのAtomや非同期処理を取り入れることが重要です。このセクションでは、コードを洗練させるための実践的なテクニックを紹介します。

リードオンリーモードのAtom


リードオンリーモードのAtomは、他のAtomの値を計算して導出する際に便利です。例えば、複数の状態を組み合わせて表示する場合に使用します。

リードオンリーAtomの作成


以下の例では、2つの数値を加算するリードオンリーAtomを定義します:

import { atom } from 'jotai';

export const num1Atom = atom(0);
export const num2Atom = atom(0);

// リードオンリーAtomを定義
export const sumAtom = atom((get) => get(num1Atom) + get(num2Atom));

利用例


リードオンリーAtomを利用して計算結果を表示するコンポーネント:

import React from 'react';
import { useAtom } from 'jotai';
import { num1Atom, num2Atom, sumAtom } from './store';

const Calculator = () => {
  const [num1, setNum1] = useAtom(num1Atom);
  const [num2, setNum2] = useAtom(num2Atom);
  const [sum] = useAtom(sumAtom);

  return (
    <div>
      <input
        type="number"
        value={num1}
        onChange={(e) => setNum1(Number(e.target.value))}
      />
      <input
        type="number"
        value={num2}
        onChange={(e) => setNum2(Number(e.target.value))}
      />
      <p>合計: {sum}</p>
    </div>
  );
};

export default Calculator;

非同期処理を伴うAtom


Jotaiでは、非同期操作をAtomで直接管理できます。これにより、APIコールやデータフェッチを簡潔に組み込むことができます。

非同期Atomの作成


以下の例では、APIからデータを取得する非同期Atomを作成します:

import { atom } from 'jotai';

export const fetchDataAtom = atom(async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
  const data = await response.json();
  return data;
});

利用例


非同期Atomを利用してデータを表示するコンポーネント:

import React from 'react';
import { useAtom } from 'jotai';
import { fetchDataAtom } from './store';

const FetchDataComponent = () => {
  const [data] = useAtom(fetchDataAtom);

  return (
    <div>
      <h2>取得したデータ</h2>
      <p>{data.title}</p>
    </div>
  );
};

export default FetchDataComponent;

リードオンリーモードと非同期処理を組み合わせた例


例えば、非同期で取得したデータに基づいてリードオンリーAtomを作成することも可能です:

import { atom } from 'jotai';

export const fetchDataAtom = atom(async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
  const data = await response.json();
  return data;
});

export const titleAtom = atom((get) => {
  const data = get(fetchDataAtom);
  return data.title;
});

これにより、複雑なデータ処理をシンプルに表現できます。

まとめ

  • リードオンリーAtom:計算結果や導出状態を簡潔に管理できる。
  • 非同期処理:APIコールなどの非同期タスクをAtomで直感的に扱える。
  • 組み合わせの柔軟性:複数のAtomを連携させることで、高度な状態管理が可能。

JotaiのリードオンリーAtomと非同期Atomを活用することで、さらに効率的で保守性の高い状態管理を実現できます。次のセクションでは、カスタムAtomの作成と高度な応用例について解説します。

カスタムAtomで独自の状態管理を構築

Jotaiの柔軟性を活かすためには、カスタムAtomを作成し、独自の状態管理ロジックを組み込むことが重要です。カスタムAtomを使用すれば、特定の状態に対する高度な操作や条件付きの状態管理が可能になります。このセクションでは、カスタムAtomの作成方法と活用例を紹介します。

カスタムAtomの作成

カスタムAtomは、基本のatom関数に状態の取得(get)と更新(set)のロジックを追加することで作成します。

基本的なカスタムAtomの例


以下は、カウンターの増加量をカスタマイズできるカスタムAtomの例です:

import { atom } from 'jotai';

// ベースとなるAtom
const baseCountAtom = atom(0);

// カスタムAtom
export const customCountAtom = atom(
  (get) => get(baseCountAtom), // 現在の値を取得
  (get, set, newIncrement) => {
    const currentCount = get(baseCountAtom);
    set(baseCountAtom, currentCount + newIncrement); // 指定された増加量を反映
  }
);

使用例


このカスタムAtomを使用するコンポーネント:

import React from 'react';
import { useAtom } from 'jotai';
import { customCountAtom } from './store';

const CustomCounter = () => {
  const [count, setCount] = useAtom(customCountAtom);

  return (
    <div>
      <p>現在のカウント: {count}</p>
      <button onClick={() => setCount(1)}>1増加</button>
      <button onClick={() => setCount(5)}>5増加</button>
    </div>
  );
};

export default CustomCounter;

条件付きロジックのカスタムAtom


カスタムAtomは条件付きロジックを組み込むことで、より複雑な状態管理が可能です。

例:カウンターの上限値を設定する


以下のカスタムAtomは、状態が10を超えないように制限をかけます:

import { atom } from 'jotai';

const baseCountAtom = atom(0);

export const limitedCountAtom = atom(
  (get) => get(baseCountAtom),
  (get, set, newIncrement) => {
    const currentCount = get(baseCountAtom);
    const updatedCount = currentCount + newIncrement;

    if (updatedCount <= 10) {
      set(baseCountAtom, updatedCount); // 上限値内なら更新
    } else {
      console.warn("カウントは10を超えることができません");
    }
  }
);

非同期処理を含むカスタムAtom


非同期操作をAtomに組み込むことで、サーバーとの通信や遅延のある処理を管理できます。

例:APIコールを伴う状態更新


以下は、API経由でデータを保存するカスタムAtomです:

import { atom } from 'jotai';

export const apiAtom = atom(
  null,
  async (get, set, newValue) => {
    const response = await fetch('https://api.example.com/update', {
      method: 'POST',
      body: JSON.stringify({ value: newValue }),
    });
    if (response.ok) {
      set(apiAtom, newValue); // APIコール成功時に状態を更新
    } else {
      console.error("データの保存に失敗しました");
    }
  }
);

使用例


非同期処理を伴うAtomを使用するコンポーネント:

import React, { useState } from 'react';
import { useAtom } from 'jotai';
import { apiAtom } from './store';

const SaveDataComponent = () => {
  const [value, setValue] = useState('');
  const [, saveData] = useAtom(apiAtom);

  return (
    <div>
      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
      <button onClick={() => saveData(value)}>データを保存</button>
    </div>
  );
};

export default SaveDataComponent;

まとめ


カスタムAtomを活用することで、以下が実現できます:

  • 条件付きロジック:特定の条件に基づいた状態変更。
  • 非同期操作の組み込み:APIコールや遅延処理を直接状態管理に統合。
  • 状態操作の柔軟性向上:特定の操作にカスタムロジックを簡単に追加。

カスタムAtomを使うことで、Reactアプリケーションの状態管理をより強力かつ柔軟に進化させられます。次のセクションでは、Jotaiと他のライブラリとの連携方法を解説します。

Jotaiと他のツールとの連携:React QueryやReduxとの比較

Jotaiは、Reactアプリケーションの状態管理をシンプルにするライブラリですが、他のツールとの連携や比較を理解することで、適切な状況で活用できます。このセクションでは、React QueryReduxと比較しながら、Jotaiの連携可能性と選択のポイントを解説します。

JotaiとReact Queryの連携

React Queryは、サーバーサイドデータのフェッチやキャッシングを効率的に行うライブラリです。Jotaiと組み合わせることで、ローカル状態とサーバーサイドデータを統合的に管理できます。

React Queryの導入


まず、React Queryをプロジェクトに追加します:

npm install @tanstack/react-query

React QueryとJotaiの組み合わせ例


以下のコード例では、React Queryを利用してデータをフェッチし、その結果をJotaiのAtomで管理します:

import { atom } from 'jotai';
import { useQuery } from '@tanstack/react-query';

export const dataAtom = atom(async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
  const data = await response.json();
  return data;
});

export const useDataQuery = () => useQuery(['data'], async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
  return response.json();
});

React Queryをデータ取得に使用し、Jotaiでローカル状態を管理することで、両者の利点を組み合わせることができます。

JotaiとReduxの比較

Reduxは、長年にわたり広く使用されてきた状態管理ライブラリです。JotaiはReduxと比べて軽量でシンプルですが、それぞれ異なる用途に向いています。

Reduxの特徴

  • グローバルな状態管理:大規模なアプリケーションでの状態共有が得意。
  • 豊富なエコシステム:Middlewareや開発ツールが充実。

Jotaiの特徴

  • シンプルなAPI:学習コストが低く、小規模から中規模のアプリケーションに最適。
  • React Hooksとの親和性:Reactの自然な拡張として動作。

どちらを選ぶべきか?

  • Jotaiを選ぶ場合:
  • 小規模から中規模のアプリケーション。
  • シンプルさと柔軟性を重視。
  • Reduxを選ぶ場合:
  • 大規模なアプリケーション。
  • 高度なカスタマイズや複雑なデータフローが必要。

Jotaiとその他のライブラリの使い分け

  • MobX:状態管理の柔軟性では似ていますが、MobXはデコレーターを多用するため、好みが分かれることがあります。JotaiはよりシンプルでReact Hooksに沿った設計。
  • Zustand:Jotaiと同様に軽量で、カスタムフックをベースとした設計ですが、Jotaiは非同期処理や依存関係の管理に優れる。

Jotaiの連携可能性のメリット

  • React Queryとの併用:サーバーサイドデータを効率的に扱いつつ、ローカル状態をシンプルに管理。
  • ReduxやMobXと補完的に利用:必要に応じて複数のツールを組み合わせて使用可能。
  • カスタマイズ性:他のライブラリと柔軟に統合できる設計。

まとめ

  • Jotaiは軽量でシンプルなため、小規模から中規模のプロジェクトで特に効果を発揮します。
  • React QueryやReduxなどのライブラリと連携することで、用途に応じた最適な状態管理を実現可能です。
  • 状況に応じて、複数のライブラリを組み合わせて活用することで、柔軟で効率的なアプリケーション開発が可能になります。

次のセクションでは、Jotaiを実際のタスク管理アプリの構築に応用する方法を紹介します。

実践例:タスク管理アプリの構築

ここでは、Jotaiを活用してシンプルなタスク管理アプリを構築する方法を解説します。この実践例を通じて、Jotaiを利用した状態管理の実際の動きを体験できます。

アプリケーションの要件


タスク管理アプリでは、以下の機能を実装します:

  1. タスクの追加。
  2. タスクの表示。
  3. タスクの完了状態の更新。
  4. タスクの削除。

ステップ1: Atomの作成


まず、タスクの状態を管理するAtomを作成します。

import { atom } from 'jotai';

// タスクリストを管理するAtom
export const tasksAtom = atom([]);

ステップ2: タスクを追加する機能


タスクを追加するフォームコンポーネントを作成します。

import React, { useState } from 'react';
import { useAtom } from 'jotai';
import { tasksAtom } from './store';

const AddTask = () => {
  const [tasks, setTasks] = useAtom(tasksAtom);
  const [taskText, setTaskText] = useState('');

  const addTask = () => {
    if (taskText.trim()) {
      setTasks([...tasks, { id: Date.now(), text: taskText, completed: false }]);
      setTaskText('');
    }
  };

  return (
    <div>
      <input
        type="text"
        value={taskText}
        onChange={(e) => setTaskText(e.target.value)}
        placeholder="タスクを入力"
      />
      <button onClick={addTask}>追加</button>
    </div>
  );
};

export default AddTask;

ステップ3: タスクの表示


追加されたタスクを一覧で表示します。

import React from 'react';
import { useAtom } from 'jotai';
import { tasksAtom } from './store';

const TaskList = () => {
  const [tasks] = useAtom(tasksAtom);

  return (
    <ul>
      {tasks.map((task) => (
        <li key={task.id}>
          <span style={{ textDecoration: task.completed ? 'line-through' : 'none' }}>
            {task.text}
          </span>
        </li>
      ))}
    </ul>
  );
};

export default TaskList;

ステップ4: タスクの完了状態を更新


タスクの完了状態を切り替える機能を追加します。

const ToggleTask = ({ taskId }) => {
  const [tasks, setTasks] = useAtom(tasksAtom);

  const toggleComplete = () => {
    setTasks(
      tasks.map((task) =>
        task.id === taskId ? { ...task, completed: !task.completed } : task
      )
    );
  };

  return <button onClick={toggleComplete}>完了切り替え</button>;
};

ステップ5: タスクの削除


タスクを削除する機能を追加します。

const DeleteTask = ({ taskId }) => {
  const [tasks, setTasks] = useAtom(tasksAtom);

  const deleteTask = () => {
    setTasks(tasks.filter((task) => task.id !== taskId));
  };

  return <button onClick={deleteTask}>削除</button>;
};

ステップ6: アプリの統合


全てのコンポーネントを統合してアプリを完成させます。

import React from 'react';
import AddTask from './AddTask';
import TaskList from './TaskList';

const App = () => (
  <div>
    <h1>Jotaiで作るタスク管理アプリ</h1>
    <AddTask />
    <TaskList />
  </div>
);

export default App;

実装結果

  • タスクを入力して追加できます。
  • タスクの完了状態を更新できます。
  • タスクを削除できます。

まとめ


このタスク管理アプリを通じて、以下を学びました:

  • Jotaiを使った状態管理の実装方法。
  • タスクの追加、更新、削除といった一般的な状態管理の実践。

JotaiのシンプルなAPIにより、柔軟で保守性の高い状態管理が簡単に実現できます。次のセクションでは、この記事のまとめに入ります。

まとめ

本記事では、Reactアプリケーションにおける効率的な状態管理を実現するために、Jotaiの基本的な使い方から高度な応用までを解説しました。JotaiのシンプルなAPIと柔軟性により、軽量な状態管理が可能になり、Reactアプリ開発の効率が向上します。

具体的には、以下の内容を学びました:

  • Jotaiの特徴と他ライブラリとの比較。
  • Atomを用いた基本的な状態管理。
  • リードオンリーAtomや非同期処理、カスタムAtomの活用方法。
  • タスク管理アプリの実装を通じた実践的な使い方。

Jotaiは、シンプルでありながら高度なカスタマイズが可能な状態管理ツールです。これを活用することで、Reactプロジェクトをより直感的かつ効率的に構築できるでしょう。ぜひプロジェクトでJotaiを試してみてください。

コメント

コメントする

目次