Recoil核心概念 #

Recoil 的架构 #

Recoil 的架构设计非常简洁,核心概念只有三个:

text
┌─────────────────────────────────────────────────────────────┐
│                        Recoil 架构                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ┌─────────────┐     ┌─────────────┐     ┌─────────────┐  │
│   │    Atom     │────▶│  Selector   │────▶│  Component  │  │
│   │  (状态单元)  │     │ (派生状态)   │     │  (React组件) │  │
│   └─────────────┘     └─────────────┘     └─────────────┘  │
│         │                   │                   │          │
│         └───────────────────┴───────────────────┘          │
│                             │                              │
│                    ┌────────▼────────┐                     │
│                    │   RecoilRoot    │                     │
│                    │   (状态容器)     │                     │
│                    └─────────────────┘                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Atom(原子状态) #

什么是 Atom? #

Atom 是 Recoil 中最小的状态单元,类似于 React 的 useState,但它是全局的,可以被任何组件访问和修改。

Atom 的特点 #

特点 说明
独立性 每个 Atom 都是独立的状态单元
可读可写 可以读取和修改 Atom 的值
全局性 任何组件都可以访问 Atom
响应式 Atom 变化时,订阅它的组件会重渲染

创建 Atom #

jsx
import { atom } from 'recoil';

const countState = atom({
  key: 'countState',
  default: 0,
});

Atom 配置选项 #

jsx
const userState = atom({
  key: 'userState',
  default: null,
  dangerouslyAllowMutability: false,
  effects: [
    ({ onSet }) => {
      onSet((newValue, oldValue) => {
        console.log(`Value changed from ${oldValue} to ${newValue}`);
      });
    },
  ],
});
选项 类型 说明
key string 唯一标识符,必须全局唯一
default any 默认值或 Promise
dangerouslyAllowMutability boolean 是否允许直接修改对象
effects array 副作用函数数组

使用 Atom #

jsx
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';

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

function CountDisplay() {
  const count = useRecoilValue(countState);
  return <span>Count: {count}</span>;
}

function IncrementButton() {
  const setCount = useSetRecoilState(countState);
  return <button onClick={() => setCount(c => c + 1)}>+</button>;
}

Selector(选择器) #

什么是 Selector? #

Selector 是一个纯函数,它接受 Atom 或其他 Selector 作为输入,返回一个派生值。当依赖的状态变化时,Selector 会自动重新计算。

Selector 的特点 #

特点 说明
派生状态 基于其他状态计算得出
自动缓存 相同输入返回缓存结果
自动追踪依赖 自动检测依赖的状态
可异步 支持异步计算

创建只读 Selector #

jsx
import { selector } from 'recoil';

const doubleCountState = selector({
  key: 'doubleCountState',
  get: ({ get }) => {
    const count = get(countState);
    return count * 2;
  },
});

创建可写 Selector #

jsx
const tempCelsiusState = atom({
  key: 'tempCelsiusState',
  default: 25,
});

const tempFahrenheitState = selector({
  key: 'tempFahrenheitState',
  get: ({ get }) => {
    const celsius = get(tempCelsiusState);
    return celsius * 9 / 5 + 32;
  },
  set: ({ set }, fahrenheit) => {
    const celsius = (fahrenheit - 32) * 5 / 9;
    set(tempCelsiusState, celsius);
  },
});

使用 Selector #

jsx
function TemperatureConverter() {
  const [celsius, setCelsius] = useRecoilState(tempCelsiusState);
  const [fahrenheit, setFahrenheit] = useRecoilState(tempFahrenheitState);
  
  return (
    <div>
      <input
        value={celsius}
        onChange={(e) => setCelsius(Number(e.target.value))}
      />
      °C
      <input
        value={fahrenheit}
        onChange={(e) => setFahrenheit(Number(e.target.value))}
      />
      °F
    </div>
  );
}

RecoilRoot(状态容器) #

什么是 RecoilRoot? #

RecoilRoot 是 Recoil 的状态容器组件,它创建了一个状态存储上下文,所有使用 Recoil 的组件必须在其内部。

RecoilRoot 的作用 #

text
┌─────────────────────────────────────────┐
│              RecoilRoot                 │
│  ┌───────────────────────────────────┐  │
│  │         状态存储 (Store)           │  │
│  │  ┌─────────┐  ┌─────────┐        │  │
│  │  │ Atom A  │  │ Atom B  │        │  │
│  │  └─────────┘  └─────────┘        │  │
│  │  ┌─────────┐  ┌─────────┐        │  │
│  │  │Selector │  │Selector │        │  │
│  │  │   C     │  │   D     │        │  │
│  │  └─────────┘  └─────────┘        │  │
│  └───────────────────────────────────┘  │
│                                         │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐ │
│  │Component│  │Component│  │Component│ │
│  │    A    │  │    B    │  │    C    │ │
│  └─────────┘  └─────────┘  └─────────┘ │
└─────────────────────────────────────────┘

RecoilRoot 配置 #

jsx
import { RecoilRoot } from 'recoil';

function App() {
  return (
    <RecoilRoot
      initializeState={({ set }) => {
        set(countState, 10);
        set(userState, { name: 'John' });
      }}
    >
      <YourApp />
    </RecoilRoot>
  );
}

状态流 #

数据流向 #

Recoil 采用单向数据流:

text
         ┌──────────────┐
         │   Atom       │
         │  (状态源)     │
         └──────┬───────┘
                │
                ▼
         ┌──────────────┐
         │  Selector    │
         │ (派生状态)    │
         └──────┬───────┘
                │
                ▼
         ┌──────────────┐
         │  Component   │
         │  (消费者)     │
         └──────┬───────┘
                │
                │ set()
                │
                ▼
         ┌──────────────┐
         │   Atom       │
         │  (状态更新)   │
         └──────────────┘

状态更新流程 #

jsx
const countState = atom({ key: 'count', default: 0 });

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

执行流程:

  1. 用户点击按钮
  2. 调用 setCount(count + 1)
  3. Recoil 更新 Atom 的值
  4. 通知所有订阅该 Atom 的组件
  5. 组件重渲染

依赖图 #

Recoil 自动构建状态依赖图:

jsx
const firstNameState = atom({
  key: 'firstName',
  default: 'John',
});

const lastNameState = atom({
  key: 'lastName',
  default: 'Doe',
});

const fullNameState = selector({
  key: 'fullName',
  get: ({ get }) => {
    const first = get(firstNameState);
    const last = get(lastNameState);
    return `${first} ${last}`;
  },
});

依赖关系:

text
firstNameState ──┐
                 ├──▶ fullNameState
lastNameState ───┘

firstNameStatelastNameState 变化时,fullNameState 会自动重新计算。

细粒度更新 #

Recoil 实现了真正的细粒度更新:

jsx
const nameState = atom({
  key: 'name',
  default: { firstName: 'John', lastName: 'Doe' },
});

function FirstName() {
  const name = useRecoilValue(nameState);
  return <span>{name.firstName}</span>;
}

function LastName() {
  const name = useRecoilValue(nameState);
  return <span>{name.lastName}</span>;
}

注意:Atom 是整体更新的,如果需要细粒度更新,应该拆分为多个 Atom:

jsx
const firstNameState = atom({ key: 'firstName', default: 'John' });
const lastNameState = atom({ key: 'lastName', default: 'Doe' });

异步状态 #

Recoil 原生支持异步状态:

jsx
const userQuery = selector({
  key: 'userQuery',
  get: async ({ get }) => {
    const userId = get(userIdState);
    const response = await fetch(`/api/users/${userId}`);
    return response.json();
  },
});

function UserProfile() {
  const user = useRecoilValue(userQuery);
  return <div>{user.name}</div>;
}

Loadable 状态 #

异步状态有三种状态:

jsx
const userLoadable = useRecoilValueLoadable(userQuery);

switch (userLoadable.state) {
  case 'hasValue':
    return <div>{userLoadable.contents.name}</div>;
  case 'loading':
    return <div>Loading...</div>;
  case 'hasError':
    return <div>Error: {userLoadable.contents.message}</div>;
}

总结 #

Recoil 的核心概念非常简洁:

概念 作用
Atom 最小状态单元,可读可写
Selector 派生状态,自动追踪依赖
RecoilRoot 状态容器,提供上下文

这些概念组合起来,可以构建复杂的状态管理系统。

下一步,让我们学习 Atom基础,深入了解 Atom 的使用方法。

最后更新:2026-03-28