Facebook 最近内部释出一个状态管理库Recoil。心里着实吓了一跳,笔者花费了一段时间的实践选定了hox,并且完成了一套纯函数组件、纯hook、按钮级权限、微前端开发架子:X-neuron/antdFront 。这么看岂不是又得推翻重造,于是忍不住加了两天班,深度补脑了下。
对比之前,首先想先搞明白,那么上一代是啥:redux。由此衍生出来的dva、rematch都是redux的思想,通过数据流框架来优化代码的组织结构的产物。归纳下就是 action,dispatch,connect,reducer,useStore,Provider,Context 这些东西,然后再其上些做排列组合。概念不少,学习成本不低。所以,什么是下一代?,得至少得满足跟上一代得组合要素不同这个基本条件。
1、背后团队对比
Recoil来自Fb官方实验项目。hox来自蚂蚁金服体验技术部。两位背后的爸爸都有丰富的实战经验。
2、start 总数
截止发稿日期 Recoil: 4.9k , hox:364 ,但笔者认为星星不是衡量一个项目的标准,有很多方案,为解决某些特定问题而生,往往曲高和寡,星星少的可怜,加上国内现存的react项目数据流多都是redux时代的dva、rematch的忠实客户,重写数据流往往意味着代码完全重构。大家看vue星星很快很多,因为强框架,脚手架,使用简单,学习成本低,也适合各类培训机构,所以星星自然多了,但从 虚拟Dom的diff 到 hook ,React一直都是行业的创新领导者。
3、思想对比
都是订阅和Hook的两者结合方法。解决了 context 状态的传导需要过多的provider 嵌套的问题。
4、API对比
hox 仅仅1个API, Recoil 11个API。学习成本上,hox吊打Recoil…
5、文档对比
Recoil有官方文档,详细介绍其设计原理; hox 相关思想的介绍,资源较少,可能API 太简洁,1页ReadMe就能讲看明白的东西,为什么要做个网页…通过通读源码能取到点真经,这点上看,Recoil吊打Hox。
6、应用场景对比
Recoil ModeL的定义及使用;
import {
atom, useRecoilState} from 'recoil';
const counter = atom({
key: 'myCounter',
default: 0,
});
function Counter() {
const [count, setCount] = useRecoilState(counter);
const incrementByOne = () => setCount(count + 1);
return (
<div>
Count: {
count}
<br />
<button onClick={
incrementByOne}>Increment</button>
</div>
);
}
hox Model的定义及使用
任意一个 custom Hook ,用 createModel 包装后,就变成了持久化,且全局共享的数据。
import {
createModel } from 'hox';
/* 任意一个 custom Hook */
function useCounter() {
const [count, setCount] = useState(0);
const decrement = () => setCount(count - 1);
const increment = () => setCount(count + 1);
return {
count,
decrement,
increment
};
}
export default createModel(useCounter)
createModel 返回值是个 Hook,你可以按 React Hooks 的用法正常使用它。
import {
useCounterModel } from "../models/useCounterModel";
function App(props) {
const counter = useCounterModel();
return (
<div>
<p>{
counter.count}</p>
<button onClick={
counter.increment}>Increment</button>
</div>
);
}
useCounterModel 是一个真正的 Hook,会订阅数据的更新。也就是说,当点击 “Increment” 按钮时,会触发 counter model 的更新,并且最终通知所有使用useCounterModel 的组件或 Hook。
总结:model的定义和使用上,recoil 通过atom包装原生的object。hox 拥抱了是原生的hook。两者都贴近原生。 都很不错。
状态复用
recoil 通过 selector 选择多个atom定义的state,然后计算下,输出到订阅更新的组件中。
const tempFahrenheit = atom({
key: 'tempFahrenheit',
default: 32,
});
const tempCelcius = selector({
key: 'tempCelcius',
get: ({
get}) => ((get(tempFahrenheit) - 32) * 5) / 9,
set: ({
set}, newValue) => set(tempFahrenheit, (newValue * 9) / 5 + 32),
});
function TempCelcius() {
const [tempF, setTempF] = useRecoilState(tempFahrenheit);
const [tempC, setTempC] = useRecoilState(tempCelcius);
const addTenCelcius = () => setTempC(tempC + 10);
const addTenFahrenheit = () => setTempF(tempF + 10);
return (
<div>
Temp (Celcius): {
tempC}
<br />
Temp (Fahrenheit): {
tempF}
<br />
<button onClick={
addTenCelcius}>Add 10 Celcius</button>
<br />
<button onClick={
addTenFahrenheit}>Add 10 Fahrenheit</button>
</div>
);
}
hox通过hook的复用,来实现复用。纯原生…
import {
useCounterModel } from"./useCounterModel";
export functionuseCounterDouble() {
const counter = useCounterModel.data;
return {
...counter,
count: counter.count * 2
};
}
两者复用的区别,在于 hox 通过hook来复用,Rcoil的使用方式需要两个API的组合,较为繁琐。这点上hox优于Rcoil。
笔者向hox团队建议扩展API,支持默认值输入后,这种纯逻辑的复用性,在Recoil里需要一个额外的初值,和一个额外的selector 才能实现。
import {
createModel } from'hox';
/* 任意一个 custom Hook */
import {
createModel } from 'hox';
import {
useBoolean } from '@umijs/hooks';
// 比如toggle的类应当具有通用性
function useToggle(defValue) {
const {
state, toggle, setTrue, setFalse } = useBoolean(defValue ?? true);
const set = (bool) => (bool ? setTrue : setFalse)
return {
state, toggle, set }
}
export const useSiderMenuToggleModel = createModel(useToggle,true);
export const useTabsToggleModel = createModel(useToggle,false);
Recoil剩下还有9个API,都是一些业务细节API,不影响对比,时间上 hox和Rcoil 都能轻松应付以下场景:
Flexible shared state: 在 react tree 任意的地方都能灵活共享 state
Derived data and queries: 高效可靠地根据变化的 state 进行计算
App-wide state observation: time travel debugging, 支持 undo, 日志持久化
综上所述,hox 和 Rcoil都和上一代的数据流组织元素完全不同。完全配的上下一代的称呼。至于学习使用,hox 目测优于 Rcoil.