brickspert / blog

个人技术博客,博文写在 Issues 里。
4.07k stars 548 forks source link

hox - 下一代 React 状态管理器 #32

Open brickspert opened 4 years ago

brickspert commented 4 years ago

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 来定义的,它有以下几个特性:

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

其它

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
  };
}

经典用户故事

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

如果你用 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>
  )
}
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 逻辑库。

myywlc commented 4 years ago

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

brickspert commented 4 years ago

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

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

3lang3 commented 4 years ago

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

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

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