Recoil.js
使用 Recoil 会为你创建一个数据流向图,从 atom(共享状态)到 selector(纯函数),再流向 React 组件。Atom 是组件可以订阅的 state 单位。selector 可以同步或异步改变此 state。
Recoil 定义了一个有向图 (directed graph),正交同时又天然连结于你的 React 树上。状态的变化从该图的顶点(我们称之为 atom)开始,流经纯函数 (我们称之为 selector) 再传入组件。
开发目的
出于兼容性和简便性的考虑,相比使用外部的全局状态,使用 React 内置的状态管理能力是个最佳的选择。但是 React 有这样一些局限性:
- 组件间的状态共享只能通过将 state 提升至它们的公共祖先来实现,但这样做可能导致重新渲染一颗巨大的组件树。
- Context 只能存储单一值,无法存储多个各自拥有消费者的值的集合。
- 以上两种方式都很难将组件树的顶层(state 必须存在的地方)与叶子组件 (使用 state 的地方) 进行代码分割。
希望改善上述的问题的同时,不仅能保留 API 以及语义,还能使其的表现尽可能保持 React 的样子。
基于下面的实现,Recoil 诞生了
- 可以定义无需模板代码的 API,共享的状态拥有与 React 本地 state 一样简单的 get/set 接口 (当然如果需要,也可以使用 reducer 等进行封装)。
- 有了与 Concurrent 模式及其他 React 新特性兼容的可能性。
- 状态的定义是渐进式和分布式的,这使代码分割成为可能。
- 无需修改对应的组件,就能将它们本地的 state 用派生数据替换。
- 无需修改对应的组件,就能将派生数据在同步与异步间切换。
- 能将导航视为头等概念,甚至可以将状态的转变编码进链接中。
- 可以很轻松地以可回溯的方式持久化整个应用的状态,持久化的状态不会因为应用的改变而丢失。
安装
NPM
Recoil 的 package 已上传至 npm。欲下载最新版本,请运行如下命令:
$ npm install recoil
或者可以使用 yarn:
$ yarn add recoil
Bundler
通过 npm 安装的 Recoil 与 Webpack 或 Rollup 等模块 bunlder 可以轻松匹配。
支持 ES5
Recoil 的构建没有转译成 ES5,我们也不支持在 ES5 下使用 Recoil。如果你需要兼容不支持 ES6 的浏览器,你可以通过 Babel 编译你的代码,并使用 @babel/preset-env
的 preset。然而,我们不建议你这样做,你可能会遇到问题。
主要是,像 React 这类的库,Recoil 依赖于 Map 和 Set 类型以及其他 ES6 的特性。使用 polyfill 来模拟这些特性可能会导致性能大大降低。
CDN
从 0.0.11 版本开始,Recoil 提供了 UMD 构建,可以直接在 <script>
中使用,并将符号 Recoil 暴露给全局的命名空间。我们建议链接到一个特定的版本号和构建,以避免较新版本带来的破坏性更改:
<script src="https://cdn.jsdelivr.net/npm/recoil@0.0.11/umd/recoil.production.js"></script>
你可以浏览 jsdelivr CDN 上所有的 Recoil 文件。
ESLint
如果你在你的项目中使用了 eslint-plugin-react-hooks。例如,在 eslint 中的配置如下:
// previous .eslint config
{
"plugins": [
"react-hooks"
],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}
建议将 'useRecoilCallback'
添加到 additionalHooks
列表中。这样做,当传递给 useRecoilCallback()
的依赖关系出错时,ESLint 会发出警告,并建议进行修复。additionalHooks
的格式是一个 regex 字符串。
// modified .eslint config
{
"plugins": [
"react-hooks"
],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": [
"warn", {
"additionalHooks": "useRecoilCallback"
}
]
}
}