We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
说到状态管理器,轮子满天飞。在 Class 时代,redux 与 mobx 几乎占据了全部市场,几乎没有没用过 redux 的同学。随着 Hooks 的诞生,新的一批轮子应运而生,其中有代表性的有 unstated-next、constate 等等。当然无论什么轮子,要解决的问题都是一样的:**跨组件状态共享。**在解决这个核心问题的同时,需要尽可能的满足以下几个特性:
最近,facebook 官方出了一个状态管理器解决方案 Recoil,我们来体验一下。
使用 Recoil,我们需要在项目最外层包一个 RecoilRoot ,这个和大部分状态管理器一致,通过 context 来跨组件传递数据。
RecoilRoot
import React from 'react'; import { RecoilRoot } from 'recoil'; function App() { return ( <RecoilRoot> ... </RecoilRoot> ); }
状态最简单的就是定义和使用。在 Recoil 中,通过 atom 来定义一个状态。
atom
const inputValueState = atom({ key: "inputValue", default: "" });
如上面的代码所示,我们定义了一个 inputValue 状态,它的默认值是空字符串。需要注意的是 key 字段,它应该是全局唯一的。这个 key 主要为了 debug 方便,持久化数据(数据恢复时的唯一标识),以及可以方便的看到全局 atoms 树。 消费状态也比较简单,通过 useRecoilState 来消费状态。
inputValue
key
useRecoilState
import React from "react"; import { useRecoilState } from "recoil"; import { inputValue } from "../store"; const InputA = () => { const [value, setValue] = useRecoilState(inputValueState); return <input value={value} onChange={e => setValue(e.target.value)} />; }; export default InputA;
是不是很简单?Recoil 的基础用法就是这样的。我在这里写了一个 demo,你可以体验下。
有些状态需要依赖其它状态,这时候就要用 selector 来定义这个状态了。比如,我们需要定义一个新的状态 filterdInputValue ,它是过滤 inputValue 中的数字后的值。
selector
filterdInputValue
const filterdInputValue = selector({ key: "filterdInputValue", get: ({get}) => { // 通过 get 可以读取其它状态 const inputValue = get(inputValueState); return inputValue.replace(/[0-9]/ig, ""); }, });
selector 比较简单,就是为了实现状态的依赖。你可以在这个 demo 体验下。
良好的异步请求支持是状态管理器必不可少的。Recoil 提供了一个 useRecoilValueLoadable 来处理异步请求。直接上例子:
useRecoilValueLoadable
const currentUserNameQuery = selector({ key: "CurrentUserName", get: async () => { const response = await queryUserInfo(); return response.name; } });
我们需要通过 selector 来定义异步状态,如果 get 函数是一个 Promise,则代表该状态为异步状态,需要使用 useRecoilValueLoadable 来消费该状态。
get
const UserName = () => { const userNameLoadable = useRecoilValueLoadable(currentUserNameQuery); switch (userNameLoadable.state) { case "hasValue": return <div>{userNameLoadable.contents}</div>; case "loading": return <div>Loading...</div>; case "hasError": throw userNameLoadable.contents; } };
从上面例子可以看到, useRecoilValueLoadable 返回的状态,可以通过 state 字段读取到异步请求的状态。我写了个 demo,你可以体验下。 当然通过 useRecoilValueLoadable 来消费异步状态,比较符合我们当前的习惯。但 Recoil 更推荐通过 React.Suspense 来消费异步状态,这里就仁者见仁了,虽然 Suspense 可能是方向,但用起来是还不太习惯。
state
React.Suspense
Suspense
const UserName = () => { const userName = useRecoilValue(currentUserNameQuery); return <>{userName}</> } }; function MyApp() { return ( <React.Suspense fallback={<div>Loading...</div>}> <UserName /> </React.Suspense> ); }
当前 Recoil 还处于开发阶段,文档都还不是很全。基于现状,说几点我的感受。
这点我很惊讶,也是写这个文章的时候才发现的,很奇怪。讲道理 Recoil 支持 typescript 应该是顺手的事情,可能后期需要来个 @types/recoil 吧。
@types/recoil
这个特性应该是必备的,应该不会彻底抛弃 Class 组件。估计下个版本肯定会支持的这个特性的。实现成本较低,不支持的话就太反人类了。
各类 API 一共有 19 个,偏复杂了。感觉很多都是可以合并的,比如 atom 和 selector 合并成一个等等(也可能是我考虑不成熟)。建议官方可以考虑精简精简,本来是一个很简单的东西,搞的太复杂了。
我们需要消费一个状态的时候,需要 import 两个东西,比较繁琐。
import { useRecoilState } from "recoil"; import { inputValueState } from "../store"; // 用法 useRecoilState(inputValueState);
本来应该可以直接通过字符串 key 消费的,但这样和 redux 问题一样了,无法支持 ts。
import { useRecoilState } from "recoil"; useRecoilState('inputValueState');
无论如果,import 两个东西不是一个好的用法。
没有看到让人眼前一亮的东西,没有使用冲动。静观发展~
Recoil 整体看下来,比较中庸,需要静观发展。另外推荐一下我目前正在用的最简单的 React 状态管理器 hox,只有一个 API,非常符合直觉,没有任何上手成本,完全拥抱 Hooks 😋。
关注公众号「前端技术砖家」,拉你进交流群,大家一起共同交流和进步。
The text was updated successfully, but these errors were encountered:
您好,我觉得文章写的非常不错,很有帮助。 但是有一点可能需要纠正一下,就是recoil并不是一个所谓的“官方”产物
Sorry, something went wrong.
No branches or pull requests
说到状态管理器,轮子满天飞。在 Class 时代,redux 与 mobx 几乎占据了全部市场,几乎没有没用过 redux 的同学。随着 Hooks 的诞生,新的一批轮子应运而生,其中有代表性的有 unstated-next、constate 等等。
当然无论什么轮子,要解决的问题都是一样的:**跨组件状态共享。**在解决这个核心问题的同时,需要尽可能的满足以下几个特性:
Recoil 体验
最近,facebook 官方出了一个状态管理器解决方案 Recoil,我们来体验一下。
准备工作
使用 Recoil,我们需要在项目最外层包一个
RecoilRoot
,这个和大部分状态管理器一致,通过 context 来跨组件传递数据。跨组件状态共享
状态最简单的就是定义和使用。在 Recoil 中,通过
atom
来定义一个状态。如上面的代码所示,我们定义了一个
inputValue
状态,它的默认值是空字符串。需要注意的是
key
字段,它应该是全局唯一的。这个key
主要为了 debug 方便,持久化数据(数据恢复时的唯一标识),以及可以方便的看到全局 atoms 树。消费状态也比较简单,通过
useRecoilState
来消费状态。是不是很简单?Recoil 的基础用法就是这样的。我在这里写了一个 demo,你可以体验下。
状态互相依赖
有些状态需要依赖其它状态,这时候就要用
selector
来定义这个状态了。比如,我们需要定义一个新的状态
filterdInputValue
,它是过滤inputValue
中的数字后的值。selector
比较简单,就是为了实现状态的依赖。你可以在这个 demo 体验下。异步支持
良好的异步请求支持是状态管理器必不可少的。Recoil 提供了一个
useRecoilValueLoadable
来处理异步请求。直接上例子:我们需要通过
selector
来定义异步状态,如果get
函数是一个 Promise,则代表该状态为异步状态,需要使用useRecoilValueLoadable
来消费该状态。从上面例子可以看到,
useRecoilValueLoadable
返回的状态,可以通过state
字段读取到异步请求的状态。我写了个 demo,你可以体验下。当然通过
useRecoilValueLoadable
来消费异步状态,比较符合我们当前的习惯。但 Recoil 更推荐通过React.Suspense
来消费异步状态,这里就仁者见仁了,虽然Suspense
可能是方向,但用起来是还不太习惯。评价
优点
不足
当前 Recoil 还处于开发阶段,文档都还不是很全。基于现状,说几点我的感受。
1. 没有使用 ts 实现,目前不支持 ts
这点我很惊讶,也是写这个文章的时候才发现的,很奇怪。讲道理 Recoil 支持 typescript 应该是顺手的事情,可能后期需要来个
@types/recoil
吧。2. 目前没有支持 Class 组件消费状态。
这个特性应该是必备的,应该不会彻底抛弃 Class 组件。估计下个版本肯定会支持的这个特性的。实现成本较低,不支持的话就太反人类了。
3. API 偏多,有一定上手成本。
各类 API 一共有 19 个,偏复杂了。感觉很多都是可以合并的,比如
atom
和selector
合并成一个等等(也可能是我考虑不成熟)。建议官方可以考虑精简精简,本来是一个很简单的东西,搞的太复杂了。4. 消费较繁琐
我们需要消费一个状态的时候,需要 import 两个东西,比较繁琐。
本来应该可以直接通过字符串
key
消费的,但这样和 redux 问题一样了,无法支持 ts。无论如果,import 两个东西不是一个好的用法。
5. 没有足够的亮点
没有看到让人眼前一亮的东西,没有使用冲动。静观发展~
后记
Recoil 整体看下来,比较中庸,需要静观发展。
另外推荐一下我目前正在用的最简单的 React 状态管理器 hox,只有一个 API,非常符合直觉,没有任何上手成本,完全拥抱 Hooks 😋。
❤️感谢阅读
关注公众号「前端技术砖家」,拉你进交流群,大家一起共同交流和进步。
The text was updated successfully, but these errors were encountered: