我为什么要用 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'// 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/