React状态管理:Recoil
在React
中有时需要不同层级的组件状态共享,虽然context
可以实现,但在比较复杂的场景中有点乏力,所以慢慢的变出现了状态管理,比较常用的状态管理库有:Redux
、mobx
。但这些库其实都是第三方维护,在React Europe 2020 Conference
上,Facebook
开源了一款状态管理Recoil
。
特点
极简的React设计风格
:拥有与React
一样的工作方式与原理。将其添加到应用中可获取快速、灵活的状态共享数据流图
:针对派生数据(Derived data)和异步查询采用纯函数以及高效订阅的方式进行处理应用程序全局监听
:通过监听应用程序中所有状态的变化来实现持久化存储,路由,时间旅行调试或撤销,并且不会影响代码分割
安装
npm install recoil // or yarn add recoil
核心概念
Recoil
创建一个数据流有向图,状态的变化从该图的顶点(atom(共享状态)
)开始,流经selector(纯函数)
,再流向React
组件。Atom
是组件可以订阅的state
,selector
可以同步或异步改变此state
。可以大概想象一下,数据流形成的向图:一个atom
被多个组件订阅,则此atom
指向每个组件,第二个也被多个组件订阅,则第二个atom
也指向多个组件,这两个atom
的被订阅组件可能是同一个,也可能不是同一个,则便形成了atom
指向多个组件的交叉向图。
Recoil
的概念很少,只有Atom
和Selector
,下面依次了解一下
Atom
Atom
是状态的单位,可以更新也可订阅,当atom
被更新,每个被订阅的组件都会用新值重新渲染。可用atom
来代替组件内部状态,当多个组件使用相同的atom
,则这些组件共享atom
的状态。
创建
Atom
由Recoil.atom
函数创建,接收一个对象:
key
:标识当前atom
的唯一key
值,请记住:key
需要保持全局唯一default
:默认值dangerouslyAllowMutability
:设置允许atom
中的对象可变,对象变化不代表status
的变化
import { atom } from 'recoil
const countState = atom({
key:'count',
default: 0
})
使用
创建的atom
不能直接使用,需要通过Recoil
提供的hook
来使用,比如:需要读写atom
的状态值,则可以使用Recoil.useRecoilState
函数
import React from 'react';
import { atom, RecoilRoot, useRecoilState } from 'recoil';
const countState = atom({
key: 'count',
default: 0
});
const Counter = () => {
const [count, setCount] = useRecoilState(countState);
return (
<>
<button onClick={() => setCount((count) => count - 1)}>-</button>
<span>{count}</span>
<button onClick={() => setCount((count) => count + 1)}>+</button>
</>
);
};
const App = () => {
return (
<RecoilRoot>
<Counter />
</RecoilRoot>
);
};
export default App;
注意:所有使用
Recoil
的组件需要使用RecoilRoot
包裹
上述例子和使用useState
比起来似乎没有什么变化只是新创建了一个countState
并通过useRecoilState
使用,但是countState
是可以多组件共享的,为了实现多组件共享,将上述代码改造一下。
store/count.ts
import { atom } from 'recoil';
export default atom({
key: 'count',
default: 0
});
components/Count/index.tsx
import React from 'react';
import { useRecoilValue } from 'recoil';
import coutnState from '../../store/count';
const Count = () => {
const count = useRecoilValue(coutnState);
return <span>{count}</span>;
};
export default Count;
components/Count/CountButton.tsx
import React from 'react';
import { useSetRecoilState } from 'recoil';
import coutnState from '../../store/count';
const CountButton = (props: { type: string }) => {
const setCount = useSetRecoilState(coutnState);
const onAdd = () => setCount((count) => count + 1);
const onDec = () => setCount((count) => count - 1);
const text = props.type === 'add' ? '+' : '-';
const onClick = props.type === 'add' ? onAdd : onDec;
return <button onClick={onClick}>{text}</button>;
};
export default CountButton;
App.tsx
import React from 'react';
import { RecoilRoot } from 'recoil';
import Count from './components/Count';
import CountButton from './components/Count/CountButton';
const App = () => {
return (
<RecoilRoot>
<CountButton type="dec" />
<Count />
<CountButton type="add" />
</RecoilRoot>
);
};
export default App;
通过改造的代码之后,可以看出只要使用Recoil
用于状态管理是多么简单
Selector
Selector
是一个纯函数,入参是atom
或selector
。当传入的atom
或selector
更新时,将会重新执行selector
函数重新渲染组件,类似Vue
中的compoted
。一般被用于计算基于state
的派生数据,可避免冗余的state
。
创建
Selector
由Recoil.selector
创建,接收一个对象:
key
:同atom
的key
get
:用于计算的函数,接收一个对象,对象中有一个get
函数用于获取其他的atom
或selector
的值
import React from 'react';
import { atom, RecoilRoot, selector, useRecoilState, useRecoilValue } from 'recoil';
const countState = atom({
key: 'count',
default: 0
});
const doubleCountSelector = selector({
key: 'doubleCount',
get: ({ get }) => get(countState) * 2
});
const Count = () => {
const [count, setCount] = useRecoilState(countState);
const doubleCount = useRecoilValue(doubleCountSelector);
return (
<>
<div>
<button onClick={() => setCount((count) => count - 1)}>-</button>
<span>count: {count}</span>
<button onClick={() => setCount((count) => count + 1)}>+</button>
</div>
<div>double count: {doubleCount}</div>
</>
);
};
const App = () => {
return (
<RecoilRoot>
<Count />
</RecoilRoot>
);
};
export default App;
上述只是Selector
的基础用法,还有更多的Selector
用法,比如set
,异步Selector
,此文章只是先简单了解一下Recoil
。