Recoil - Facebook 官方 React 状态管理器

说到状态管理器,轮子满天飞。在 Class 时代,redux 与 mobx 几乎占据了所有市场,几乎没有没用过 redux 的同窗。随着 Hooks 的诞生,新的一批轮子应运而生,其中有表明性的有 unstated-next、constate 等等。
固然不管什么轮子,要解决的问题都是同样的:**跨组件状态共享。**在解决这个核心问题的同时,须要尽量的知足如下几个特性:前端

  • TypeScript 支持
  • 友好的异步支持
  • 支持状态互相依赖
  • 同时支持 Class 与 Hooks 组件
  • 使用简单

Recoil 体验

最近,facebook 官方出了一个状态管理器解决方案 Recoil,咱们来体验一下。 react

准备工做

使用 Recoil,咱们须要在项目最外层包一个 RecoilRoot ,这个和大部分状态管理器一致,经过 context 来跨组件传递数据。git

import React from 'react';
import { RecoilRoot } from 'recoil';

function App() {
  return (
    <RecoilRoot>
        ...
    </RecoilRoot>
  );
}
复制代码

跨组件状态共享

状态最简单的就是定义和使用。在 Recoil 中,经过 atom 来定义一个状态。github

const inputValueState = atom({
  key: "inputValue",
  default: ""
});
复制代码

如上面的代码所示,咱们定义了一个 inputValue 状态,它的默认值是空字符串。
须要注意的是 key 字段,它应该是全局惟一的。这个 key 主要为了 debug 方便,持久化数据(数据恢复时的惟一标识),以及能够方便的看到全局 atoms 树。

消费状态也比较简单,经过 useRecoilState 来消费状态。typescript

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,你能够体验下。
redux

2020-05-17 13.09.49.gif

状态互相依赖

有些状态须要依赖其它状态,这时候就要用 selector 来定义这个状态了。
好比,咱们须要定义一个新的状态 filterdInputValue ,它是过滤 inputValue 中的数字后的值。异步

const filterdInputValue = selector({
  key: "filterdInputValue",
  get: ({get}) => {
    // 经过 get 能够读取其它状态
    const inputValue = get(inputValueState);
    return inputValue.replace(/[0-9]/ig, "");
  },
});
复制代码

selector 比较简单,就是为了实现状态的依赖。你能够在这个 demo 体验下。
async

2020-05-17 13.29.55.gif

异步支持

良好的异步请求支持是状态管理器必不可少的。Recoil 提供了一个 useRecoilValueLoadable 来处理异步请求。直接上例子:函数

const currentUserNameQuery = selector({
  key: "CurrentUserName",
  get: async () => {
    const response = await queryUserInfo();
    return response.name;
  }
});
复制代码

咱们须要经过 selector 来定义异步状态,若是 get 函数是一个 Promise,则表明该状态为异步状态,须要使用 useRecoilValueLoadable 来消费该状态。atom

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,你能够体验下。

2020-05-17 15.34.53.gif


固然经过 useRecoilValueLoadable 来消费异步状态,比较符合咱们当前的习惯。但 Recoil 更推荐经过 React.Suspense 来消费异步状态,这里就仁者见仁了,虽然 Suspense 多是方向,但用起来是还不太习惯。

const UserName = () => {
  const userName = useRecoilValue(currentUserNameQuery);
  return <>{userName}</>
 }
};
function MyApp() {
  return (
    <React.Suspense fallback={<div>Loading...</div>}>
      <UserName />
    </React.Suspense>
  );
}
复制代码

评价

优势

  • 以前状态管理器满天飞,若是官方能一统天下,应该算一件好事情。
  • 对 React concurrent 模式支持良好。

不足

当前 Recoil 还处于开发阶段,文档都还不是很全。基于现状,说几点个人感觉。

1. 没有使用 ts 实现,目前不支持 ts

这点我很惊讶,也是写这个文章的时候才发现的,很奇怪。讲道理 Recoil 支持 typescript 应该是顺手的事情,可能后期须要来个 @types/recoil 吧。

2. 目前没有支持 Class 组件消费状态。

这个特性应该是必备的,应该不会完全抛弃 Class 组件。估计下个版本确定会支持的这个特性的。实现成本较低,不支持的话就太反人类了。

3. API 偏多,有必定上手成本。

image.png

各种 API 一共有 19 个,偏复杂了。感受不少都是能够合并的,好比 atom 和 selector 合并成一个等等(也多是我考虑不成熟)。建议官方能够考虑精简精简,原本是一个很简单的东西,搞的太复杂了。

4. 消费较繁琐

咱们须要消费一个状态的时候,须要 import 两个东西,比较繁琐。

import { useRecoilState } from "recoil";
import { inputValueState } from "../store";

// 用法
useRecoilState(inputValueState);
复制代码

原本应该能够直接经过字符串 key 消费的,但这样和 redux 问题同样了,没法支持 ts。

import { useRecoilState } from "recoil";

useRecoilState('inputValueState');
复制代码

不管若是,import 两个东西不是一个好的用法。

5. 没有足够的亮点

没有看到让人眼前一亮的东西,没有使用冲动。静观发展~

后记

Recoil 总体看下来,比较中庸,须要静观发展。
另外推荐一下我目前正在用的最简单的 React 状态管理器 hox,只有一个 API,很是符合直觉,没有任何上手成本,彻底拥抱 Hooks 😋。

感谢阅读

关注公众号「前端技术砖家」,拉你进交流群,你们一块儿共同交流和进步。

相关文章
相关标签/搜索