前言
Recoil是FaceBook公司提出的状态管理方案,相当于和React是师出同门,虽然我目前在工作当中用的是Mobx,并且使用起来也是十分的爽,但是Recoil
非常有必要去了解一下,所以在学习之后写一篇文章记录一下!
我们都知道React
强调的是immuteable
,而Recoil
强调的同样也是immuteable
,而mobx
则是mutable
,而immuteable
给带来的好处就是增强组件整体的应用性能,相对来说,确定可变对象是否变更是复杂的,因为如果对一个可变的对象,我们需要判断它是否改变,这就需要当前对象与先前副本进行比较,我们需要遍历整颗对象树,并且比较每个变量和值,这个过程将会变得很复杂,而确定不可变对象是否改变是非常容易的,我们只需要判断对象的引用是否相同,如果引用改变了那就改变了,经此而已。对React
来说,因为使用的是不可变数据,那么对UI
渲染来说,判断组件是否重新渲染则来的更容易些。
关于Recoil
Recoil
采用分散管理原子状态的设计模式
Recoil
提出了一个新的管理状态单位Atom
,它是可更新和订阅的,当一个Atom
更新之后,每个订阅它的组件都会与之更新重新渲染,如果多个组件使用同一个Atom
,那么这些组件将会共享他们的状态。
因为 React
本身提供的 state
状态在跨组件状态相对来时是比较困难,当然我们可以用context
,所以我们在开发时一般借助一些其他的库如 Redux、Mobx
来帮助我们管理状态。这些库目前正被广泛使用,我们也并没有遇到什么大问题,那么 Facebook
为什么还要推出一款新的状态管理框架呢?
使用 Redux、Mobx
当然可以,并没有什么问题,主要原因是它们本身并不是 React
库,我们是借助这些库的能力来实现状态管理。像 Redux
它本身虽然提供了强大的状态管理能力,但是使用的成本非常高,你还需要编写大量冗长的代码,另外像异步处理或缓存计算也不是这些库本身的能力,甚至需要借助其他的外部库。
并且,它们并不能访问 React
内部的调度程序,而 Recoil
在后台使用 React
本身的状态,在未来还能提供并发模式这样的能力。
Recoil基础
初始化
使用Recoil
的组件需要使用RecoilRoot
组件包裹起来
import React from 'react';
import {
RecoilRoot,
atom,
selector,
useRecoilState,
useRecoilValue,
useSetRecoilState
} from 'recoil';
function App() {
return (
<RecoilRoot>
<CharacterCounter />
</RecoilRoot>
);
}
定义状态
上面我们已经提到了 Atom
的概念, Atom
是一种新的状态,但是和传统的 state
不同,它可以被任何组件订阅,当一个 Atom
被更新时,每个被订阅的组件都会用新的值来重新渲染。
export const nameState = atom({
key: 'nameState',
default: '栗鼠怪'
});
其中 key
必须在 RecoilRoot
作用域内唯一,也可以认为是 state 树打平时 key 必须唯一的要求。
default
定义默认值,既然数据定义分散了,默认值定义也是分散的。
订阅和更新状态
useRecoilState
:类似 useState 的一个Hook
,可以取到atom
的值以及setter
函数useSetRecoilState
:只获取setter
函数,如果只使用了这个函数,状态变化不会导致组件重新渲染useRecoilValue
:只获取状态
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>;
}
从上面我们可以看到我们取值和改变值是使用hook
的形式,对于习惯react
函数组件来说是非常友好的
派生状态
selector
表示一段派生状态,它使我们能够建立依赖于其他 atom
的状态。它有一个强制性的 get
函数,其作用与 redux
的 reselect
, MobX
的 @computed
, 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
,我们当可以从Recoil
学到关于React
状态管理的基本功,它的设计模式是分散式管理,这里有点像mobx
,但是在使用方式上完全拥抱react
函数式中Hook
的使用,关于派生状态我们可以使用recoil
的selector
,也可以使用useMemo
。毕竟是 Facebook
官方推出的状态管理框架,其主打的是高性能以及可以利用 React
内部的调度机制,包括其承诺即将会支持的并发模式,这一点还是非常值得期待的。最近笔者也有去了解RTK
,即redux-toolkit
,下篇文章应该会去记录和学习它的使用!