名符其实的react下一代状态管理器hox

5,095 阅读4分钟

前言

自从React16版本发布Hooks以来,大家纷纷上车尝鲜。毫无疑问,Hooks在一定程度上解决了组件间功能和逻辑复用的问题,在组件间的逻辑的封装和复用确实真香,但Hooks在数据状态的共享方法略有不足,虽然可以使用useReducer实现数据状态管理,但在一定程度上是对redux的思想的复用。我们知道reduxFluxdva等这些React状态管理的工具,实际上都是对actiondispatchreduceruseStoreProviderContext这些概念的排列组合,概念太多,学习入手成本较高,项目使用都差不多,会有产生许多的模版代码。

hox

既然如此是否有学习成本比较低,入手简单的针对Hooks的状态管理器呢?答案是有,其中来自来自蚂蚁金服体验技术部hox就是这样一种工具。下面我们从学习、上手、原理几个方法聊聊这个被定为为下一代React状态管理器,看看其是否符合其定位的目标。

学习

hox来自蚂蚁金服体验技术部,其背后的团队在React各种实践场景上都有很丰富的经验,因此其后续的维护和迭代还是很靠谱的。可能因为其只有一个API,因此其文档也是十分简单的,一眼就能看到头了。这对于我们前端的开发者而言就是很友好的,由于千变万化的前端,各种轮子、各种技术层出不穷,前端的娃娃们表示学不动了。而这种只有一个API的工具,我们表示还是可以学的动的。hox的详细文档可以参看github上的readme支持中英文,链接如下:

  1. 中文文档github.com/umijs/hox/b…
  2. 英文文档github.com/umijs/hox/b…

特性

hox作为下一代的状态管理器,其具有如下特性:

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

上手

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 hookcustom 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原理图

总结

简言之,hox大道至简,只有一个API,但其既能满足逻辑的封装和复用,又能满足状态复用和管理,值得尝试的状态管理器。