こんにちは!フロントエンドの現場を渡り歩いているチーフアーキテクトの私です。
Reactを触り始めて、「よし、コンポーネントを分けて画面を作れるぞ!」となったあたりで、必ずと言っていいほどぶつかる壁がありますよね。そう、「コンポーネントをまたいでデータを共有するにはどうすればいいんだ問題」です。
親から子へ、子から孫へとprops(プロパティ)のバケツリレーを延々と続け、コードがスパゲッティのようになって頭を抱えた経験、ありませんか?「大丈夫ですよ」、みんな最初はそこで盛大につまずきます。私だって昔は夜な夜な画面に向かってため息をついていました。
そんな複雑怪奇になりがちな状態管理の救世主の一つとして、Meta(旧Facebook)が開発した状態管理ライブラリが「Recoil(リコイル)」です。
今回は、そのRecoilの心臓部である「Atom(アトム)」と「Selector(セレクター)」について、難しい専門用語はちょっと脇に置いて、お買い物の身近な例え話と一緒に優しく紐解いていきましょう!
—
1. アプリの状態管理って、たとえるなら「お財布とレシート」のようなもの
React標準の`useState`は、いわば「自分専用のポケットマネー」です。そのコンポーネントの中だけで使うお小遣いなら、ポケットマネーで十分。
でも、アプリケーションが大きくなってくると、「ヘッダーのショッピングカートアイコン」と「商品の詳細画面」と「お会計画面」のすべてで、同じ「今カゴに入っている商品のデータ」を共有したくなりますよね。
ここで登場するのがRecoilです。Recoilの世界では、データ管理の考え方がとてもシンプルに整理されています。
- Atom(アトム): アプリ全体で共有する「お財布(データの本体)」
- Selector(セレクター): お財布の中身から計算して導き出す「レシートや合計金額(派生したデータ)」
この2つを味方につけると、コンポーネントのツリー構造の深さに悩まされることなく、必要なデータをどこからでもスッと取り出せるようになります。さあ、それぞれの正体をもう少し詳しく見ていきましょう。
—
2. Atom(アトム)とは?:アプリ全体の「共通のお財布」
物質の最小単位「原子」の名前を持つ理由
Atomという言葉は、物理の授業で習った「原子」から来ています。RecoilにおけるAtomは、「状態(State)の最小単位」です。
身近な例でたとえるなら、スーパーのレジの横にある「今日のたまごの価格が書かれたボード」のようなものです。このボードの数字が変われば、お店の中のポップも、レジの計算も、ぜんぶ連動して変わりますよね。
Reactのコードで書くときは、こんなふうに「キー(識別名)」と「初期値」をポツンと定義してあげます。
import { atom } from ‘recoil’;
// これが「お財布(Atom)」の定義です!
// uniqueなkey(一意の名前)と、初期値(今回は数字の0)を設定します。
export const countState = atom({
key: ‘countState’, // ほかのAtomと被らない名前なら何でもOKです
default: 0, // 最初に入っている値
});
これだけで、この`countState`というAtomは、アプリのどのコンポーネントからでも「今の値ちょうだい!」「値を書き換えたい!」とアクセスできる共有のプラットフォームになります。
—
3. Selector(セレクター)とは?:計算係の「お利口さんなアシスタント」
元のデータから「計算された結果」を作る
Atomがただの「データの入れ物」だとすると、Selectorは何をしてくれるのでしょうか?
たとえるなら、スーパーのレジ係の店員さんです。
レジ係さんは、お財布(Atom)の中身をそのまま渡すのではなく、「カゴに入った商品の合計金額(消費税込み)」を計算して私たちに教えてくれますよね。
Selectorは、「既存のAtomのデータを加工・計算して、新しいデータを生み出す仕組み」です。データそのものを二重に保存するのではなく、「計算ルール」だけを定義しておくのがポイントです。
import { selector } from ‘recoil’;
import { countState } from ‘./atoms’; // さっき作ったAtomをインポート
// Selectorは、Atomの値を元にして新しい値を「計算」します
export const doubleCountState = selector({
key: ‘doubleCountState’,
get: ({ get }) => {
// get関数を使って、元のAtomの現在地を覗き見します
const currentCount = get(countState);
// 元の値がわかったので、それを2倍にして返します(計算!✨)
return currentCount 2;
},
});
「元の値が変わったら、自動的に計算結果も連動して変わる」。これがSelectorの最高に痺れる(そして便利な)ところです。
—
4. 【実践】実際にコードを書いて動かしてみよう!
理屈はこれくらいにして、実際にReactのコンポーネントでどう使うのか、動かせるサンプルを見てみましょう。
今回は、ボタンを押すと数字が増えて、Selectorが自動でその「2倍の数字」を計算して表示してくれる簡単なカウンターを作ります。
import React from ‘react’;
import { RecoilRoot, useRecoilState, useRecoilValue, atom, selector } from ‘recoil’;
// ==========================================
// 1. Atom と Selector の定義(本当は別ファイルに書くとスッキリします)
// ==========================================
// 共有のお財布(Atom)
const countState = atom({
key: ‘countStateForSample’,
default: 1,
});
// 計算係のアシスタント(Selector)
const doubleCountState = selector({
key: ‘doubleCountStateForSample’,
get: ({ get }) => {
const count = get(countState);
return count 2; // カウントを2倍にする計算
},
});
// ==========================================
// 2. コンポーネントたち
// ==========================================
// 数字を増やすボタンを持つコンポーネント
function CounterButton() {
// useRecoilStateを使うと、値の取得(count)と更新(setCount)が両方できます
const [count, setCount] = useRecoilState(countState);
return (
今のカウント: {count}
{/ ボタンを押すとAtomの数値が「+1」されます /}
);
}
// 2倍された数字を表示するだけのコンポーネント
function DoubleCounterDisplay() {
// useRecoilValueを使うと、「読む専用」としてSelectorの値を取り出せます
const doubleCount = useRecoilValue(doubleCountState);
return (
✨Selectorが計算した2倍の値: {doubleCount}
);
}
// ==========================================
// 3. アプリの根っこ(RecoilRootで包むのがお約束!)
// ==========================================
export default function App() {
return (
/ Recoilを使うときは、一番親を
RecoilのAtomとSelector入門
);
}
チーフアーキテクトからのワンポイントアドバイス
コードの最後のほうにある `
—
5. まとめ:怖がらなくて大丈夫、まずは小さく使ってみよう
ここまで、AtomとSelectorの概念についてお話してきましたが、いかがでしたでしょうか?
- Atom は、アプリ全体で共有する「生データ(お財布)」
- Selector は、その生データを元に計算する「派生データ(レシート・計算係)」
Reactの`useState`のバケツリレーに疲れてきたら、Recoilの世界を覗いてみると、世界がパッと明るくなったようにコードがスッキリ整理されるのを実感できるはずです。
最初は「なんだかファイルが増えて難しそう…」と感じるかもしれませんが、実際に手を動かしてブラウザで画面が連動する瞬間を見ると、「おっ、なるほど!」と楽しくなってきますよ。
あなたのWeb制作・React開発の旅が、少しでも快適で楽しいものになりますように。
それでは、また次の現場でお会いしましょう!

コメント