brickspert/blog

hox - 下一代 React 状态管理器

Opened this issue · 3 comments

github: https://github.com/umijs/hox

别着急喷,我已经能想到你为什么会进来看这个文章了,当你看到这个题目的时候,你一定会有几连问:

基于 React Hooks 状态管理器的轮子太多了,你们再造一个有什么意思?

我并不是针对某个轮子,我只想说现有所有的轮子都囿于 reduxunstated-next 的**,无非就是 actiondispatchreduceruseStoreProviderContext 这些东西,在这些东西上做排列组合。概念一大堆,理解成本不低,用起来还都差不多。

为什么你敢说你们是“下一代”?

hox 够简单,一个 API,几乎无学习成本。够好用,你会用 Hooks,就会用 hox。我想象不到比我们更简单,更好用的轮子怎么造出来?

不想看,不想学,学不动了,咋办?

一个 API,眼睛一瞪就会用,没有任何学习成本。

你们够权威吗?你们会弃坑吗?

hox 的开发者来自蚂蚁金服体验技术部,我们有 umi、dva、antd、antv 等一堆开源软件,团队足够权威。

同时 hox 的**足够简单,放心用好了。

你们能完全替代 redux,dva 吗?

状态管理器解决的问题都一样,用 hox 完全可以实现所有需求。

hox 介绍

hox 是完全拥抱 React Hooks 的状态管理器,model 层也是用 custom Hook 来定义的,它有以下几个特性:

  • 只有一个 API,简单高效,几乎无需学习成本
  • 使用 custom Hooks 来定义 model,完美拥抱 React Hooks
  • 完美的 TypeScript 支持
  • 支持多数据源,随用随取

下面我们进入正题,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)

使用 Model

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 之间互相依赖怎么写了,就是单纯的 Hooks 互相依赖,自然而然咯。
import { useCounterModel } from "./useCounterModel";

export function useCounterDouble() {
  const counter = useCounterModel();
  return {
    ...counter,
    count: counter.count * 2
  };
}
  • 只读不订阅更新就更简单了。
import { useCounterModel } from "./useCounterModel";

export function useCounterDouble() {
  const counter = useCounterModel.data;
  return {
    ...counter,
    count: counter.count * 2
  };
}
  • 支持在 class 组件使用哦。

经典用户故事

你肯定遇到过这样的场景:

  • 一开始你把逻辑和数据都放在组件中,每次组件重建,数据都会重置掉。
  • 某一天,你想在把数据存起来,每次组件重建,不会重新刷新数据了。
  • 假设你用的 redux,你需要重新翻译一遍逻辑,完全重写逻辑和数据层,不知道有多痛苦。

如果你用 hox,故事就完全不一样了,你只需要把逻辑和数据层代码直接复制出去就完事了。

  • 比如你开始的代码是这样的:
const CountApp = () => {
  const [count, setCount] = useState(0)
  const decrement = () => setCount(count - 1)
  const increment = () => setCount(count + 1)

  return (
    <div>
      count: {count}
      <button onClick={increment}>自增</button>
      <button onClick={decrement}>自减</button>
    </div>
  )
}
  • 如果你想持久化数据,每次进来想恢复上一次的 count,把逻辑代码复制出来,用 createModel 包一层就完事了。
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
  };
}
/* 用 createModel 包一下就行 */
export default createModel(useCounter)
import { useCounterModel } from "./useCounterModel";

export function CountApp() {
  const {count, increment, decrement} = useCounterModel();
  return (
    <div>
      count: {count}
      <button onClick={increment}>自增</button>
      <button onClick={decrement}>自减</button>
    </div>
  )
}

总结

讲完了,核心内容很短,因为足够简单,更多内容可以见 github。如果你觉得 redux、dva 等太难学习,使用繁琐,如果你觉得 unstated-next Provider 嵌套太多,太乱的话,不妨试试 hox,保证会给你全新的开发体验。

hox,下一代 React 状态管理器。

文末再打个广告:umi hooks 最好的 react hooks 逻辑库。

专家来个hox源码讲解。。。,喜欢你的讲解

专家来个hox源码讲解。。。,喜欢你的讲解

我先推广一下,后面再讲解。原理比较简单的。

既然逻辑都放Hooks了,状态当然更应该放在Hooks,为Hox点赞👍
小困惑🤔️:
对于组件内使用Modal是否和自定义Hooks太过相似了,在团队内开发除了通过命名规范来区分想不到其他更好的办法了,是否可以考虑通过统一Hooks对外暴露:

import { useHoxModal } from 'hox'
import { counterModel } from "./counterModel";
// component
const counter = useHoxModal(counterModel)

这样套一层的话,增加了一定的代码量,但是可读性更强了
统一出口也方便以后拓展的
@brickspert