我为什么要用 Recoil?

起因是最近重构了一个 props 传递层级十分深,组件之间状态通信十分频繁的详情页面。整个代码梳理下来,很难保护,重构时思考,做组件细分,把数据单拎进去,对数据做一个涣散治理。

比照了下之前用过的 Redux、Mobx 感觉有点重,因为我的项目 纯 ts + Func hooks 模式重构,利用这两种比拟惯例的状态管理工具有两个弊病:

  1. 对于我的项目代码侵入比拟大,要编写大量的状态治理代码(dispatch、action、reducer)
  2. 因为是内部库,它们并不能拜访 React 外部的调度程序

次要还是感觉这两个库,对于我的项目应用来说,有点重了,没必要为了一个不算很简单的数据管理和通信引入并不算小的 npm 依赖包。起初思考用 Context 来解决,然而多个组件订阅要写多个 Provider 或者有一个根组件的 Provider 来接入数据,这就有会导致很多不必要的重绘和代码量。刚好有共事提了一嘴 Recoil(fb 本人为 react 提供的状态管理工具库),看了下材料,感觉跟本人的我的项目符合度还不错,遂试用了下,成果不错。


简略的介绍下 Recoil

Recoil是FaceBook公司提出的状态治理计划(个人感觉像是给 react Func Comps 量身打造的轻量级状态管理工具)。
咱们都晓得React强调的是immuteable,而Recoil强调的同样也是immuteable,immuteable给带来的益处就是加强组件整体的利用性能。对于 immuteable 不是很分明的,倡议查阅相干文档,这里不做论述。

Recoil采纳扩散治理原子状态的设计模式.

Recoil提出了一个新的治理状态单位Atom(原子化),它是可更新和订阅的,当一个Atom更新之后,每个订阅它的组件都会与之更新从新渲染,如果多个组件应用同一个Atom,那么这些组件将会共享他们的状态。

从上图不难看出,它的核心理念是原子化拆分数据,通过订阅公布的模型对数据进行治理。

Recoil根底

初始化

应用Recoil的组件须要应用RecoilRoot组件包裹起来

import React from 'react';import { RecoilRoot } from 'recoil';function App() {  return (    <RecoilRoot>      <CharacterCounter />    </RecoilRoot>  );}

定义状态

下面咱们曾经提到了 Atom 的概念, Atom 是一种新的状态,然而和传统的 state 不同,它能够被任何组件订阅,当一个 Atom 被更新时,每个被订阅的组件都会用新的值来从新渲染。

export const pageInfoState = atom({  key: 'pageInfoState',  default: {}});

其中 key 必须在 RecoilRoot 作用域内惟一。
default 定义默认值。

订阅和更新状态

useRecoilState:相似 useState 的一个 Hook,能够取到 atom 的值以及 setter 函数。
useSetRecoilState:只获取 setter 函数,如果只应用了这个函数,状态变动不会导致组件从新渲染。
useRecoilValue:只获取状态。
useResetRecoilState: 重置状态。

import { nameState } from './store'// useRecoilStateconst NameInput = () => {  const [name, setName] = useRecoilState(nameState);  const onChange = (event) => {   setName(event.target.value);  };  return <>   <input type="text" value={name} onChange={onChange} />   <div>Name: {name}</div>  </>;}// useRecoilValueconst SomeOtherComponentWithName = () => {  const name = useRecoilValue(nameState);  return <div>{name}</div>;}// useSetRecoilState  const SomeOtherComponentThatSetsName = () => {  const setName = useSetRecoilState(nameState);  return <button onClick={() => setName('Jon Doe')}>Set Name</button>;}// useReSetRecoilState  const SomeOtherComponentThatSetsName = () => {  const ressetName = useSetRecoilState(nameState);  return <button onClick={ressetName}>Set Name</button>;}

从下面咱们能够看到咱们取值和扭转值是应用 hooks 的模式,对于 react 函数组件来说是十分敌对的,代码侵入十分小。

派生状态

selector 示意派生状态,它使咱们可能建设依赖于其余 atom 的状态(也能够进行一些计算操作)。它有一个强制性的 get 函数,有点相似 Vue 中的 computed 类似.

const lengthState = selector({  key: 'lengthState',   get: ({get}) => {    const text = get(nameState);    return text.length;  },});function NameLength() {  const length = useRecoilValue(charLengthState);  return <>Name Length: {length}</>;}

异步状态

Recoil 提供了通过数据流图将状态和派生状态映射到 React 组件的办法。真正弱小的性能是图中的函数也能够是异步的。这使得咱们能够在异步 React 组件渲染函数中轻松应用异步函数。应用 Recoil,你能够在选择器的数据流图中无缝地混合同步和异步性能。只需从选择器 get 回调中返回 Promise ,而不是返回值自身。

例如上面的例子,如果用户名存储在咱们须要查问的某个数据库中,那么咱们要做的就是返回一个 Promise 或应用一个 async 函数。如果 userID 产生更改,就会主动从新执行新查问。后果会被缓存,所以查问将仅对每个惟一输出执行一次(所以肯定要保障 selector 纯函数的个性,否则缓存的后果将会和最新的值不统一)。

const userNameQuery = selector({  key: 'userName',  get: async ({get}) => {    const response = await myDBQuery({      userID: get(currentUserIDState),    });    return response.name;  },});function CurrentUserInfo() {  const userName = useRecoilValue(userNameQuery);  return <div>{userName}</div>;}

总结

应用下来当前,感觉 Recoil 的分散式状态治理,有点像 observable + computed 的模式。具体源码我还没有去看过,找个工夫看过之后,再补一个源码解读。应用形式上,齐全是投合函数式组件 Hooks 的应用形式,没有提供 Component 的应用形式。

官网出品必属精品,官网文档所鼓吹的高性能和可利用 React 外部的调度机制,包含之前承诺的并发模式,还是比拟值得期待的(听听就好了)。次要还是看跟本人我的项目的符合度,适合你就用,工程和我的项目复杂度上来了,该稳重还是选稳重的状态治理计划吧,反正我下一阶段打算,把 Recoil 这玩意儿,在我的项目外面铺开体验了。

Recoil 官网文档链接

https://recoil.js.cn/