我为什么要用 Recoil?
起因是最近重构了一个 props 传递层级十分深,组件之间状态通信十分频繁的详情页面。整个代码梳理下来,很难保护,重构时思考,做组件细分,把数据单拎进去,对数据做一个涣散治理。
比照了下之前用过的 Redux、Mobx 感觉有点重,因为我的项目 纯 ts + Func hooks 模式重构,利用这两种比拟惯例的状态管理工具有两个弊病:
- 对于我的项目代码侵入比拟大,要编写大量的状态治理代码(dispatch、action、reducer)
- 因为是内部库,它们并不能拜访 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'
// useRecoilState
const 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>
</>;
}
// useRecoilValue
const 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/