前言
自从React16
版本发布Hooks
以来,大家纷纷上车尝鲜。毫无疑问,Hooks
在一定程度上解决了组件间功能和逻辑复用的问题,在组件间的逻辑的封装和复用确实真香,但Hooks
在数据状态的共享方法略有不足,虽然可以使用useReducer
实现数据状态管理,但在一定程度上是对redux
的思想的复用。我们知道redux
、Flux
、dva
等这些React
状态管理的工具,实际上都是对action
、dispatch
、reducer
、useStore
、Provider
、Context
这些概念的排列组合,概念太多,学习入手成本较高,项目使用都差不多,会有产生许多的模版代码。
hox
既然如此是否有学习成本比较低,入手简单的针对Hooks
的状态管理器呢?答案是有,其中来自来自蚂蚁金服体验技术部hox就是这样一种工具。下面我们从学习、上手、原理几个方法聊聊这个被定为为下一代React
状态管理器,看看其是否符合其定位的目标。
学习
hox
来自蚂蚁金服体验技术部,其背后的团队在React
各种实践场景上都有很丰富的经验,因此其后续的维护和迭代还是很靠谱的。可能因为其只有一个API
,因此其文档也是十分简单的,一眼就能看到头了。这对于我们前端的开发者而言就是很友好的,由于千变万化的前端,各种轮子、各种技术层出不穷,前端的娃娃们表示学不动了。而这种只有一个API
的工具,我们表示还是可以学的动的。hox
的详细文档可以参看github
上的readme
支持中英文,链接如下:
特性
hox
作为下一代的状态管理器,其具有如下特性:
- 只有一个 API,简单高效,几乎无需学习成本
- 使用 custom Hooks 来定义 model,完美拥抱 React Hooks
- 完美的 TypeScript 支持
- 支持多数据源,随用随取
上手
hox
的上手使用体验还是很不错的,因为十分简单。talk is cheap,show me code。我们直接上码看看。
import { useState } from "react";
import { createModel } from "hox";
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);
import useCounterModel from "../models/counter";
function App(props) {
const { count, increment, decrement } = useCounterModel();
return (
<div>
<p>{count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
使用hox
就是这么简单,通过createModel
包装一下将custom hook
变成share hook
,就可以在各个组件之间共享数据状态,并实现逻辑封装和复用。
原理
createModel
会创建一个Executor
组件的实例,并在执行custom hook
, custom hook
的执行结果会被保存起来。最后,它会返回一个新的share hook
即是model hook
实现数据和逻辑的共享。源码如下:
import { ModelHook, UseModel } from "./types";
import { Container } from "./container";
import { Executor } from "./executor";
import React, { useEffect, useRef, useState } from "react";
import { render } from "./renderer";
export function createModel<T, P>(hook: ModelHook<T, P>, hookArg?: P) {
// 实例化一个容器,通过发布订阅模式实现对状态改变的推送
const container = new Container(hook);
// 实例化 Executor 组件,当数据发生改变时,notify 所有订阅者进行更新
render(
<Executor
onUpdate={val => {
container.data = val;
container.notify();
}}
hook={() => hook(hookArg)}
/>
);
// useModel 这是 share hook
const useModel: UseModel<T> = depsFn => {
const [state, setState] = useState<T | undefined>(() =>
container ? (container.data as T) : undefined
);
const depsFnRef = useRef(depsFn);
depsFnRef.current = depsFn;
const depsRef = useRef<unknown[]>([]);
useEffect(() => {
if (!container) return;
function subscriber(val: T) {
if (!depsFnRef.current) {
setState(val);
} else {
const oldDeps = depsRef.current;
const newDeps = depsFnRef.current(val);
if (compare(oldDeps, newDeps)) {
setState(val);
}
depsRef.current = newDeps;
}
}
container.subscribers.add(subscriber);
return () => {
container.subscribers.delete(subscriber);
};
}, [container]);
return state!;
};
// share hook 代理 custom hook 返回的值
Object.defineProperty(useModel, "data", {
get: function() {
return container.data;
}
});
return useModel;
}
// 这是 hook 依赖项对比函数
function compare(oldDeps: unknown[], newDeps: unknown[]) {
if (oldDeps.length !== newDeps.length) {
return true;
}
for (const index in newDeps) {
if (oldDeps[index] !== newDeps[index]) {
return true;
}
}
return false;
}
其原理图如下:
总结
简言之,hox
大道至简,只有一个API
,但其既能满足逻辑的封装和复用,又能满足状态复用和管理,值得尝试的状态管理器。