React での状態管理をもっとシンプルに!Recoil 解説ガイド

recoil React
この記事は約6分で読めます。

この記事の最終更新日: 2025年5月27日

はじめに

フロントエンド開発では、コンポーネント間で状態をどのように共有するかが常に課題になります。Redux や Context API などの選択肢がありますが、Recoil は React の思想に寄り添いながらも、より直感的にグローバルステートを扱えるライブラリとして注目されています。本記事では、Recoil の基本概念から実装方法、他ライブラリとの比較、ベストプラクティスまでをわかりやすく解説します。

recoil

Recoil とは?

  • Facebook 製:2020 年に公開された比較的新しい状態管理ライブラリ。
  • 「共有可能なローカルステート」 を目指し、React コンポーネントでの状態管理をスムーズに。
  • 学習コストが低い:React のフック API とよく似た書き味で導入しやすい。

💡 ポイント:Recoil は Context API より宣言的で、Redux よりボイラープレート(記述の手間)が少ないのが特徴です。


核心概念(用語の意味を丁寧に解説)

Recoil には 3 つの中心的な用語があります。

概念役割キーワード
Atom(アトム)アプリ全体で共有できる状態の最小単位。複数のコンポーネントで同じ値を扱える“状態の源”
Selector(セレクタ)Atom や他の Selector を元に、派生した値(計算結果など)を作る関数“派生 & メモ化”
Snapshot(スナップショット)アプリの状態をある瞬間で記録・再生できる仕組み“タイムトラベル”

Atom

Atom は「状態の定義そのもの」です。React の useState に似ていますが、複数のコンポーネントで共有できます。

import { atom } from "recoil";

export const userState = atom<string | null>({
  key: "userState",        // 一意なキー(Recoil 内で重複しない必要あり)
  default: null,           // 初期値
});

Selector

Selector は Atom を元に派生した状態を計算します。たとえば「ログイン済みかどうか」などは、ユーザー情報(Atom)から判断できます。

import { selector } from "recoil";
import { userState } from "./atoms";

export const isLoggedInState = selector<boolean>({
  key: "isLoggedInState",
  get: ({ get }) => Boolean(get(userState)),
});


セットアップ手順(初心者にもわかるように)

1. Recoil のインストール

プロジェクトに Recoil を追加します。

npm install recoil
# または
yarn add recoil

2. アプリ全体を RecoilRoot で囲む

Recoil の機能を有効にするには、アプリの最上位を <RecoilRoot> で囲みます。

import { RecoilRoot } from "recoil";
import App from "./App";

export default function Root() {
  return (
    <RecoilRoot>
      <App />
    </RecoilRoot>
  );
}

3. 状態の読み書き

useRecoilState や useRecoilValue などのフックを使って、状態の操作ができます。

import { useRecoilState, useRecoilValue } from "recoil";
import { userState, isLoggedInState } from "./store";

function LoginButton() {
  const [user, setUser] = useRecoilState(userState); // 状態を読み書き
  const isLoggedIn = useRecoilValue(isLoggedInState); // 状態を読み取り専用

  return isLoggedIn ? (
    <button onClick={() => setUser(null)}>Logout</button>
  ) : (
    <button onClick={() => setUser("Alice")}>Login</button>
  );
}


非同期処理と Recoil

Recoil の Selector は async/await に対応しており、API などからデータを取得して状態として扱えます。

以下はユーザー ID に応じてユーザー情報を取得する例です:

const userProfileQuery = selector({
  key: "userProfileQuery",
  get: async ({ get }) => {
    const userId = get(userIdState);
    if (!userId) return null;
    const res = await fetch(`/api/users/${userId}`);
    return res.json();
  },
});

自動キャッシュ:同じ ID で再度呼び出された場合、API へ再リクエストせずキャッシュされたデータを返します。


Recoil DevTools でデバッグ

開発中に状態の流れを可視化するために、Chrome 拡張などの DevTools を活用できます。

また、Snapshot を使えば状態をログ出力したり、履歴として保存できます。

import { useRecoilSnapshot } from "recoil";
import { useEffect } from "react";

function DebugObserver() {
  const snapshot = useRecoilSnapshot();
  useEffect(() => {
    console.debug("Snapshot", snapshot);
  }, [snapshot]);
  return null;
}


ベストプラクティス(実務でのコツ)

  1. Atom の粒度は小さく:状態が変わるたびに再レンダリングが発生するため、1 つの Atom に複数の状態を詰め込みすぎないようにしましょう。
  2. Selector を活用してロジック分離:計算処理をコンポーネント内に書かず、Selector に移して再利用しやすくします。
  3. 型安全に設計:TypeScript を使うことで、誤った状態の扱いを防ぎます。
  4. Async Selector の管理:API 呼び出しには selectorFamily を使い、引数付きでキャッシュを細かく管理しましょう。

Recoil と他ライブラリ比較(どれを選ぶべき?)

特徴RecoilRedux ToolkitZustandJotai
学習コスト低(React に近い)中(設計の理解が必要)低(シンプルな構文)低(最小構成)
ボイラープレート少ない少ない(Toolkit 使用時)少ない非常に少ない
DevTools公式あり公式あり一部あり一部あり
型安全サポート
非同期サポート標準対応(Selector)ミドルウェア追加必要標準標準

🔍 選定ポイント:フォームや API 呼び出しが多いアプリでは Recoil の相性が良いですが、2025 年以降はメンテナンス状況も含めて再検討が必要です。


コメント

タイトルとURLをコピーしました