Recoil logo

Recoil

Meta 开源的 React 原子化状态管理库,采用原子(Atom)和选择器(Selector)模型实现细粒度状态管理,但已于 2025 年 1 月停止活跃维护并归档。

状态管理React原子化已归档

Recoil 由 Meta 于 2020 年开源,引入原子化状态管理概念,将状态分解为独立 Atom 单元,通过 Selector 实现派生状态和异步数据流。始终处于实验阶段,2025 年 1 月被归档为只读状态,Jotai 被视为其精神继承者。

Recoil

Meta 开源的 React 原子化状态管理库,采用原子(Atom)和选择器(Selector)模型实现细粒度状态管理,但已于 2025 年 1 月停止活跃维护并归档。

技术简介说明

Recoil 是由 Facebook(现 Meta)于 2020 年开源的实验性状态管理库,专为 React 应用设计。它引入了原子化状态管理的概念,将状态分解为独立的原子单元(Atom),通过选择器(Selector)实现派生状态和异步数据流。Recoil 的设计目标是解决 React Context 的性能问题,提供类似 Redux 的可预测性但更简洁的 API。

Recoil 的核心理念是"自底向上"的状态管理:从最小的原子状态开始,通过选择器组合和派生出复杂的状态逻辑。这种模式与 Jotai 非常相似,实际上 Jotai 的作者明确表示受到 Recoil 的启发。两者都采用原子模型,但 Jotai 更轻量且持续维护,而 Recoil 已在 2025 年初归档。

尽管 Recoil 在 Meta 内部(包括 Instagram)有生产环境使用案例,但它始终处于"实验性"状态,从未发布稳定的 1.0 版本。随着核心维护者 Douglas Armstrong 被 Meta 裁员,项目逐渐失去维护动力,最终在 2025 年 1 月 1 日被正式归档为只读状态。

基本信息

  • 官网https://recoiljs.org
  • GitHubhttps://github.com/facebookexperimental/Recoil
  • License:MIT
  • 最新版本:0.7.7(2023 年 4 月发布)
  • 主要维护者/公司:Meta(原 Facebook)
  • npm 周下载量:约 39 万(截至 2026 年)
  • GitHub Stars:19.5k+
  • 状态:⚠️ 已归档(Archived) - 2025 年 1 月 1 日起停止维护

快速上手

安装

# npm
npm install recoil
 
# yarn
yarn add recoil
 
# pnpm
pnpm add recoil

注意:由于 Recoil 已停止维护,不建议在新项目中使用。

基础配置

Recoil 需要在应用根部包裹 RecoilRoot

import React from 'react'
import { RecoilRoot } from 'recoil'
import App from './App'
 
function Root() {
  return (
    <RecoilRoot>
      <App />
    </RecoilRoot>
  )
}
 
export default Root

最小示例

import { atom, useRecoilState, useRecoilValue } from 'recoil'
 
// 定义原子状态
const countState = atom({
  key: 'countState',  // 唯一标识
  default: 0,         // 默认值
})
 
// 计数器组件
function Counter() {
  const [count, setCount] = useRecoilState(countState)
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(c => c + 1)}>+1</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  )
}
 
// 显示组件(只读)
function CounterDisplay() {
  const count = useRecoilValue(countState)
  return <div>Current count: {count}</div>
}
 
// 修改组件(只写)
function CounterButton() {
  const setCount = useSetRecoilState(countState)
  return <button onClick={() => setCount(c => c + 1)}>Increment</button>
}

核心概念与架构

1. Atom(原子)

Atom 是 Recoil 的核心概念,代表独立的状态单元:

const userState = atom({
  key: 'userState',
  default: {
    name: '',
    age: 0,
    isLoggedIn: false,
  },
})
 
// 使用
const [user, setUser] = useRecoilState(userState)

特点

  • 每个 Atom 有唯一 key
  • 可被多个组件订阅
  • 状态变更自动触发订阅组件重渲染
  • 支持持久化(通过 effects)

2. Selector(选择器)

Selector 用于派生状态、计算值或处理异步逻辑:

const doubledCountState = selector({
  key: 'doubledCountState',
  get: ({ get }) => {
    const count = get(countState)
    return count * 2
  },
})
 
// 异步 Selector
const currentUserState = selector({
  key: 'currentUserState',
  get: async ({ get }) => {
    const userId = get(currentUserIdState)
    const response = await fetch(`/api/users/${userId}`)
    return response.json()
  },
})

特点

  • 纯函数,依赖其他 Atom 或 Selector
  • 自动缓存,依赖不变则不重新计算
  • 支持异步操作
  • 可以设置(set)实现写入逻辑

3. RecoilRoot

提供状态管理的上下文,所有 Recoil hooks 必须在其内部使用:

<RecoilRoot>
  <App />
</RecoilRoot>
 
// 支持自定义存储
<RecoilRoot
  initializeState={(snapshot) => {
    snapshot.set(userState, { name: 'Initial User' })
  }}
>
  <App />
</RecoilRoot>

4. Hooks API

Recoil 提供多个 hooks 用于访问和修改状态:

// 读写状态
const [value, setValue] = useRecoilState(atomOrSelector)
 
// 只读状态
const value = useRecoilValue(atomOrSelector)
 
// 只写状态
const setValue = useSetRecoilState(atomOrSelector)
 
// 重置为默认值
const resetValue = useResetRecoilState(atom)
 
// 回调中访问状态
const callback = useRecoilCallback(({ snapshot, set }) => async () => {
  const value = await snapshot.getPromise(asyncSelector)
  set(resultState, value)
})
 
// 事务性更新
const [value, setValue] = useRecoilState(atom)
setValue(currentValue => newValue)

5. Atom Family

用于创建参数化的 Atom 集合:

const itemStateFamily = atomFamily({
  key: 'itemState',
  default: (id) => ({
    id,
    name: '',
    quantity: 0,
  }),
})
 
// 使用
const [item1, setItem1] = useRecoilState(itemStateFamily('item-1'))
const [item2, setItem2] = useRecoilState(itemStateFamily('item-2'))

6. Selector Family

参数化的 Selector:

const itemPriceSelectorFamily = selectorFamily({
  key: 'itemPriceSelector',
  get: (itemId) => ({ get }) => {
    const item = get(itemStateFamily(itemId))
    const exchangeRate = get(exchangeRateState)
    return item.price * exchangeRate
  },
})

核心特性

1. 原子化状态管理

将状态分解为独立的原子单元,每个 Atom 可以被多个组件订阅,实现细粒度的状态管理和精确的重渲染控制。

2. 派生状态与计算值

通过 Selector 实现派生状态,自动缓存计算结果,依赖不变时不会重新计算,类似于计算属性。

3. 异步数据流

Selector 原生支持异步操作,可以直接在 get 函数中处理 Promise,简化异步状态管理。

4. 细粒度订阅

组件只订阅实际使用的状态,状态变更时只有相关组件会重渲染,避免不必要的渲染。

5. 状态持久化

通过 Atom Effects 实现状态持久化,支持 localStorage、sessionStorage 或自定义存储。

const persistentState = atom({
  key: 'persistentState',
  default: '',
  effects: [
    ({ setSelf, onSet }) => {
      // 从 localStorage 加载
      const saved = localStorage.getItem('myKey')
      if (saved) setSelf(JSON.parse(saved))
      
      // 保存到 localStorage
      onSet((newValue) => {
        localStorage.setItem('myKey', JSON.stringify(newValue))
      })
    },
  ],
})

6. 快照与时间旅行

支持状态快照,可以实现时间旅行调试、状态序列化和恢复。

const snapshot = useRecoilSnapshot()
// 遍历所有状态
for (const node of snapshot.getNodes_UNSTABLE()) {
  console.log(node.key, snapshot.getLoadable(node))
}

7. 并发模式支持

Recoil 设计时考虑了 React 的并发特性,支持 Suspense 和 concurrent rendering。

<Suspense fallback={<div>Loading...</div>}>
  <AsyncComponent />
</Suspense>

8. 类型安全

完整的 TypeScript 支持,Atom 和 Selector 都有良好的类型推断。

const typedState = atom<{ name: string; age: number }>({
  key: 'typedState',
  default: { name: '', age: 0 },
})

9. 调试工具

提供 Recoil DevTools,支持状态检查、时间旅行调试。

10. 测试友好

状态与组件解耦,便于单元测试和集成测试。

生态图

官方生态

  • recoil - 核心库
  • @recoiljs/refine - 类型验证和转换工具

社区生态

持久化方案

  • recoil-persist - localStorage 持久化
  • recoil-sync - 同步状态到外部存储
  • recoil-nexus - 在 React 外部访问 Recoil 状态

开发工具

  • recoil-logger - 状态变更日志
  • recoil-devtools - 浏览器扩展调试工具

路由集成

  • recoil-router - 路由状态同步
  • recoil-next - Next.js 集成工具

表单集成

  • recoil-form - 表单状态管理
  • react-recoil-forms - 表单 hooks

相关项目

  • Jotai - 受 Recoil 启发的原子化状态管理,持续维护
  • Zustand - 轻量级状态管理
  • Redux Toolkit - 成熟的集中式状态管理
  • Valtio - 基于 Proxy 的状态管理

适用场景

1. 中小型 React 应用

适合需要细粒度状态管理但不需要 Redux 那样重型方案的中型应用。

注意:由于 Recoil 已停止维护,新项目建议使用 Jotai 或 Zustand。

2. 复杂派生状态

当应用有大量计算属性和派生状态时,Selector 的自动缓存机制非常有用。

3. 异步数据流

需要频繁处理异步数据获取和状态更新的场景。

4. 细粒度性能优化

需要精确控制组件重渲染,避免不必要渲染的高性能应用。

5. 状态持久化需求

需要将状态同步到 localStorage、IndexedDB 或其他持久化存储的场景。

6. 时间旅行调试

需要状态快照和回溯功能的调试场景。

7. 学习原子化状态管理

作为学习原子化状态管理概念的参考实现(尽管已停止维护)。

开发与工程化

TypeScript 支持

Recoil 提供完整的 TypeScript 类型定义:

interface User {
  id: string
  name: string
  email: string
}
 
const userState = atom<User>({
  key: 'userState',
  default: {
    id: '',
    name: '',
    email: '',
  },
})
 
// 类型推断
const [user, setUser] = useRecoilState(userState)
// user 类型自动推断为 User

代码分割

支持动态导入和代码分割:

// 动态加载 Atom
const dynamicState = atom({
  key: 'dynamicState',
  default: lazy(() => import('./heavyModule')),
})

测试策略

import { RecoilRoot } from 'recoil'
import { render, screen } from '@testing-library/react'
import { userState } from './state'
 
function TestWrapper({ children }) {
  return (
    <RecoilRoot
      initializeState={(snapshot) => {
        snapshot.set(userState, { name: 'Test User' })
      }}
    >
      {children}
    </RecoilRoot>
  )
}
 
test('displays user name', () => {
  render(<UserComponent />, { wrapper: TestWrapper })
  expect(screen.getByText('Test User')).toBeInTheDocument()
})

构建优化

Recoil 支持 Tree Shaking,未使用的功能会被自动移除。

// vite.config.js / webpack.config.js
export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          recoil: ['recoil'],
        },
      },
    },
  },
}

错误边界

<RecoilRoot>
  <ErrorBoundary fallback={<ErrorScreen />}>
    <App />
  </ErrorBoundary>
</RecoilRoot>

性能与安全

性能特点

优势

  • 细粒度订阅:组件只订阅实际使用的状态,减少不必要的渲染
  • 自动缓存:Selector 自动缓存计算结果,依赖不变时直接返回缓存值
  • 批量更新:多个状态变更会被批量处理,减少重渲染次数
  • 惰性求值:Selector 只在被访问时计算,未被使用的状态不会触发计算

性能注意事项

  • 大量 Atom 可能增加内存占用
  • 复杂的 Selector 依赖链可能导致级联更新
  • 频繁的异步 Selector 调用需要注意竞态条件

安全考虑

XSS 防护

  • Recoil 本身不直接处理 HTML,XSS 防护依赖 React 的默认转义机制
  • 持久化到 localStorage 的数据在读取时需要验证和清理

状态验证

import { refine, string, number } from '@recoiljs/refine'
 
const validatedState = atom({
  key: 'validatedState',
  default: '',
  effects: [
    ({ setSelf }) => {
      const stored = localStorage.getItem('myKey')
      if (stored) {
        const validated = refine(string())(JSON.parse(stored))
        if (validated !== null) {
          setSelf(validated)
        }
      }
    },
  ],
})

敏感数据

  • 避免将敏感信息(token、密码)持久化到 localStorage
  • 使用内存 Atom 或 session 级别的存储

技术对比

特性RecoilJotaiZustandRedux ToolkitMobX
状态模型原子化原子化单一 Store单一 Store响应式
API 风格HooksHooksHooksHooks + Reducers装饰器/Proxy
学习曲线中等中等中等
TypeScript 支持✅ 优秀✅ 优秀✅ 优秀✅ 优秀✅ 良好
包体积~14KB~2KB~1KB~10KB~16KB
DevTools✅ 基础✅ 良好✅ 良好✅ 优秀✅ 优秀
中间件❌ 有限✅ 良好✅ 良好✅ 优秀✅ 良好
异步支持✅ Selector✅ 良好✅ 良好✅ Thunk/Saga✅ Action
持久化✅ Effects✅ 良好✅ 内置✅ 需配置✅ 需配置
并发模式✅ 支持✅ 支持✅ 支持✅ 支持⚠️ 部分
维护状态❌ 已归档✅ 活跃✅ 活跃✅ 活跃✅ 活跃
社区活跃度⚠️ 下降✅ 高✅ 高✅ 高✅ 中等
适用场景已不推荐中小型应用通用大型应用复杂业务

与 Jotai 的详细对比

Recoil 和 Jotai 都采用原子化状态管理,但有重要差异:

方面RecoilJotai
维护状态已归档(2025.01)持续维护
包体积~14KB~2KB
API 复杂度较复杂更简洁
派生状态Selector派生 Atom
异步处理原生 Selector异步 Atom
React 19 支持❌ 未知✅ 完整支持
Server Components❌ 不支持✅ 支持
社区生态停滞活跃发展

迁移建议:Jotai 是 Recoil 最自然的替代方案,两者概念相似,迁移成本较低。

最佳实践

1. 状态设计原则

// ✅ 好的实践:细粒度 Atom
const userNameState = atom({ key: 'userName', default: '' })
const userAgeState = atom({ key: 'userAge', default: 0 })
 
// ❌ 不推荐:过于粗粒度的 Atom
const userState = atom({
  key: 'user',
  default: { name: '', age: 0, email: '', phone: '', /* ... */ }
})

2. 使用 Selector 缓存计算

// ✅ 好的实践:使用 Selector 缓存
const filteredListState = selector({
  key: 'filteredList',
  get: ({ get }) => {
    const list = get(listState)
    const filter = get(filterState)
    return list.filter(item => item.includes(filter))
  },
})
 
// ❌ 不推荐:在组件中直接计算
function List() {
  const list = useRecoilValue(listState)
  const filter = useRecoilValue(filterState)
  const filtered = list.filter(item => item.includes(filter)) // 每次渲染都计算
}

3. 异步数据管理

// ✅ 好的实践:使用异步 Selector
const userDataState = selector({
  key: 'userData',
  get: async ({ get }) => {
    const userId = get(currentUserIdState)
    const response = await fetch(`/api/users/${userId}`)
    if (!response.ok) throw new Error('Failed to fetch')
    return response.json()
  },
})
 
// 配合 Suspense 使用
function UserProfile() {
  const user = useRecoilValue(userDataState)
  return <div>{user.name}</div>
}
 
<Suspense fallback={<Loading />}>
  <UserProfile />
</Suspense>

4. 状态持久化

// ✅ 好的实践:使用 Atom Effects
const themeState = atom({
  key: 'theme',
  default: 'light',
  effects: [
    ({ setSelf, onSet }) => {
      const saved = localStorage.getItem('theme')
      if (saved) setSelf(saved)
      
      onSet((newValue) => {
        localStorage.setItem('theme', newValue)
      })
    },
  ],
})

5. 避免过度使用 Recoil

// ✅ 好的实践:只在需要时使用 Recoil
// 组件内部状态用 useState
function Counter() {
  const [count, setCount] = useState(0) // 不需要全局状态
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
 
// 全局状态用 Recoil
const globalUserState = atom({ key: 'globalUser', default: null })

6. 类型安全

// ✅ 好的实践:定义清晰的类型
interface Todo {
  id: string
  title: string
  completed: boolean
}
 
const todosState = atom<Todo[]>({
  key: 'todos',
  default: [],
})
 
const todoSelector = selector({
  key: 'filteredTodos',
  get: ({ get }) => {
    const todos = get(todosState)
    const filter = get(filterState)
    // TypeScript 推断 todos 为 Todo[]
    return todos.filter(todo => 
      filter === 'all' ? true : 
      filter === 'active' ? !todo.completed : todo.completed
    )
  },
})

技术局限与边界

⚠️ 已停止维护(关键问题)

Recoil 最大的局限是已于 2025 年 1 月 1 日被归档为只读状态:

  • ❌ 不会再有任何 bug 修复
  • ❌ 不会有新特性开发
  • ❌ 不会有安全补丁
  • ❌ 不会支持 React 19+ 的新特性
  • ❌ 322+ 个 open issues 永远不会被解决
  • ❌ 不会支持 React Server Components

技术局限

1. 包体积较大

相比 Jotai(~2KB)和 Zustand(~1KB),Recoil 的包体积(~14KB)较大,对性能敏感的应用可能是问题。

2. 学习曲线

相比 Zustand 的简洁 API,Recoil 的 Atom + Selector 模型需要更多学习成本。

3. 调试体验一般

虽然有 DevTools,但相比 Redux DevTools 功能较弱,调试体验不够流畅。

4. 中间件支持有限

不像 Redux 有成熟的中间件生态,Recoil 的扩展性受限。

5. React 19 兼容性未知

由于停止维护,无法保证与 React 19+ 的完全兼容性。

6. 社区生态停滞

社区贡献的插件和工具不再更新,生态系统逐渐萎缩。

迁移建议

对于现有项目:

  1. 评估迁移优先级

    • 如果项目稳定且不再需要新特性,可以暂时保持现状
    • 如果有活跃开发需求,建议尽快迁移
  2. 选择目标库

    • Jotai:最接近 Recoil 的原子化模型,迁移成本最低
    • Zustand:API 更简洁,社区更大
    • Redux Toolkit:如果需要更强的可预测性和工具链
  3. 渐进式迁移策略

    // 可以同时使用 Recoil 和 Jotai 进行渐进迁移
    <RecoilRoot>
      <JotaiProvider>
        <App />
      </JotaiProvider>
    </RecoilRoot>
  4. 迁移示例

    // Recoil
    const countState = atom({ key: 'count', default: 0 })
     
    // Jotai
    const countAtom = atom(0)

学习资源

官方资源

视频教程

文章资源

迁移指南

社区资源

2026 年现状

维护状态

Recoil 在 2026 年已经完全停止维护:

  • 归档日期:2025 年 1 月 1 日
  • 最后版本:0.7.7(2023 年 4 月)
  • 维护者状态:核心维护者 Douglas Armstrong 被 Meta 裁员后,社区成员尝试接管但最终失败
  • GitHub 状态:仓库已归档为只读,不接受新的 PR 和 Issue

Meta 的态度

Meta 内部仍在使用 Recoil(包括 Instagram 等产品),但:

  • 不再投入资源进行开源版本的维护
  • 没有计划将项目移交给社区
  • 没有发布任何官方迁移指南
  • 未来可能继续使用内部 fork 版本

与 Jotai 的关系

Jotai 是 Recoil 的精神继承者:

  • Jotai 作者明确表示受到 Recoil 启发
  • 两者都采用原子化状态管理模型
  • Jotai 保持了相似的核心概念(Atom、派生状态)
  • Jotai 持续活跃维护,支持 React 19+
  • Jotai 包体积更小(~2KB vs ~14KB)
  • Jotai API 更简洁,学习曲线更平缓

推荐迁移路径

短期策略(2026 年)

  1. 新项目:绝对不要选择 Recoil,直接使用 Jotai 或 Zustand
  2. 维护项目
    • 如果项目稳定,可以暂时保持现状
    • 如果有安全顾虑或需要新特性,开始规划迁移

中期策略(2026-2027 年)

  1. 渐进式迁移

    • 新模块使用 Jotai/Zustand
    • 逐步迁移旧模块
    • 可以同时运行多个状态管理库
  2. 迁移优先级

    • 高频更新的模块优先迁移
    • 涉及异步数据流的模块优先
    • 性能敏感的模块优先

长期策略(2027+ 年)

  1. 完全迁移:所有项目应完全迁离 Recoil
  2. 技术债务:继续使用 Recoil 将成为技术债务
  3. 人才市场:新开发者可能不熟悉 Recoil

替代方案对比

场景推荐方案理由
Recoil 迁移Jotai概念最接近,迁移成本最低
新项目ZustandJotai活跃维护,社区大
大型企业应用Redux Toolkit成熟稳定,工具链完善
简单应用ZustandAPI 简洁,学习成本低
React Server ComponentsJotaiZustand完整支持 RSC

最终建议

2026 年的明确建议:

  1. ⚠️ 不要在新项目中使用 Recoil
  2. ⚠️ 现有项目应规划迁移
  3. 优先迁移到 Jotai(概念最接近)
  4. 或迁移到 Zustand(API 更简洁)
  5. 大型企业应用考虑 Redux Toolkit

Recoil 作为实验性项目完成了它的历史使命,其原子化状态管理的理念在 Jotai 等库中得到了延续。对于开发者和团队来说,现在是时候向前看了。


文档更新时间:2026 年 7 月
数据来源:GitHub、npm、官方文档、社区讨论
状态标记:⚠️ 已归档 / 停止维护