Recoil的简单实践和思考

1,555 阅读4分钟

Recoil的简单实践和思考

背景

随着react18的发布,和react官方新文档正在逐步完善。有兴趣的小伙伴可以去react新文档的仓库issues里面查找有关于class组件的提问和回答。

其中有一条提问是:“所以,类组件会被废除吗?”。

官方回答的大意是:“未来的react新文档全面以hooks为主,至于类组件很有可能会变成一个独立的包来维护”。

(详细问答在github.com/reactjs/rea…

很明显,就是hooks的时代正式官宣。

动机

最近项目中全部使用函数组件+hooks,有一个常见的问题,想要管理一些全局状态,想找一个完美与hooks相对应的状态管理库。redux繁琐的写法个人看来与函数组件显得格格不入,集中式管理略显笨重。市面上太多各种剖析redux各种原理的热点文章,其实倒不是刻意与众不同,也不是独辟蹊径。每个人每个项目都有适合的状态管理工具。在项目技术选型中,不是人云亦云,而是自己了解项目特点,调研相关的利害关系,最后找到最适合自己项目的工具。因为吧,写出代码交付是为了赚钱;写,也得尽可能要求自己用优雅的代码赚钱~这对于自己的和公司是一种双赢的关系。血赚不亏。

我当时目的有三个:

  1. 项目不算特别大型,关于全局状态管理,尽可能越简单越好。
  2. 厌倦了redux的繁琐写法,想找一个纯hooks的全局状态管理工具。
  3. 当时出了几个开源事故新闻,尽可能官方维护稳妥一点。

然后,上网冲浪了一段时间,最后看到了recoil。

  1. 简单: 会用useState就会用recoil,上手成本极低。(顶多再来点异步和suspense)
  2. 写法: 就是useState写法,ahooks用过吧,设计传参方式差不多,似曾相识的感觉。
  3. 缘分:react是Facebook开源的吧,recoil不是react团队,但是来自于Facebook团队啊!同一个妈生的,稳。

下面就是一个最简的实践案例:

不会仔细写atom,selector和怎么包裹外层组件,官网文档写的描述非常清晰。养成看官方文档的习惯~

recoil实现后台用户信息的获取和更新

需求

用户信息有关的组件一般位于相对来说最外层的组件之中,我希望改变了用户id之后自动发送请求得到最新的用户名并渲染。

get start

定义一个atom , 你可以想象成 useState 的初始值。因为是全局的“useState“,你怎么知道它是不是唯一的,是吧,习惯性思考肯定有个唯一标识符,作为它的id之类的。

atom.js

 import { atom } from "recoil";

 export const userID = atom({
   key: "ID", // 唯一标识符
   default: 1,// 初始值
 });

初始值定义好了,当然初始值肯定有对应的方法。怎么自动根据我这个可以变化的id自动请求呢?

”根据这个id“-->"多半是作为依赖"

"根据id自动发送请求"-->”多半是此依赖会在一个封装好异步函数里面“

selector.js

 import { getUserName } from "@/api/request"; // 请求
 import { selector } from "recoil";
 import { userID } from "./atom";

 export const userName = selector({
   key: "NAME",
   get: async ({ get }) => {
     return await getUserName(get(userID)); // 一个异步的selector
   },
 });

当然这个selector可以类比成一个异步返回的 useState 的值。

有了异步的值,自然而然就要考虑到,js里面想要拿到这个值,要么就是promise全家桶处理方式,要么就是有专门获取这种异步值的方法。

翻了翻官方文档,他们也讲了这个类似的案例。

以下是作为渲染和怎么使用atomselector以及他们的关系:

User.jsx

 // ... 
 const user_name = useRecoilValueLoadable(userName);
   let username = ""; // 想要渲染
   switch (user_name.state) {
     case "hasValue":
       username = user_name.contents.data.username;
       break;
     case "loading":
       username = "";
       break;
     case "hasError":
       throw user_name.contents;
     default:
   }
 // ...
 /*
 useRecoilValueLoadable这个钩子传入一个异步的selector,内部自己处理,
 返回一个对象,根据时序异步状态的变化,会产生三个不同时刻对应的对象
 {state:"loading",contents:Promise} 
 {state:"hasValue",contents:请求成功的res} 
 {state:"hasError",contents:错误信息相关}
 */

 return <组件  username={username} >

这是异步的获取,需要搭配suspense组件

 <Suspense fallback={<Loading />}>
      <User />
 </Suspense>

最后是改变id:

  const [, setUserID] = useRecoilState(userID); // usestate的用法操作atom
  // ...
  <Button
    onClick={() => {
      setUserID(Math.random(1, 100));
    }}>
    更换userID
  </Button>
  // ...

需要的思考方式:

  1. useState使用方法举一反三的类比思考
  2. js异步操作以及async/await的使用
  3. Promise的状态
  4. react中的异步加载组件Suspense

几乎没有什么特别的需要注意的点,recoil还提供的很多api,不过实际使用,就那个几个常用。

像极了react,你会用useState和useEffect就能上手开发。(^o^)