名符其實的react下一代狀態管理器hox

前言

自從React16版本發佈Hooks以來,大家紛紛上車嚐鮮。毫無疑問,Hooks在一定程度上解決了組件間功能和邏輯複用的問題,在組件間的邏輯的封裝和複用確實真香,但Hooks在數據狀態的共享方法略有不足,雖然可以使用useReducer實現數據狀態管理,但在一定程度上是對redux的思想的複用。我們知道reduxFluxdva等這些React狀態管理的工具,實際上都是對actiondispatchreduceruseStoreProviderContext這些概念的排列組合,概念太多,學習入手成本較高,項目使用都差不多,會有產生許多的模版代碼。

hox

既然如此是否有學習成本比較低,入手簡單的針對Hooks的狀態管理器呢?答案是有,其中來自來自螞蟻金服體驗技術部hox就是這樣一種工具。下面我們從學習、上手、原理幾個方法聊聊這個被定爲爲下一代React狀態管理器,看看其是否符合其定位的目標。

學習

hox來自螞蟻金服體驗技術部,其背後的團隊在React各種實踐場景上都有很豐富的經驗,因此其後續的維護和迭代還是很靠譜的。可能因爲其只有一個API,因此其文檔也是十分簡單的,一眼就能看到頭了。這對於我們前端的開發者而言就是很友好的,由於千變萬化的前端,各種輪子、各種技術層出不窮,前端的娃娃們表示學不動了。而這種只有一個API的工具,我們表示還是可以學的動的。hox的詳細文檔可以參看github上的readme支持中英文,鏈接如下:

  1. 中文文檔:https://github.com/umijs/hox/blob/master/README-cn.md
  2. 英文文檔:https://github.com/umijs/hox/blob/master/README.md

特性

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,但其既能滿足邏輯的封裝和複用,又能滿足狀態複用和管理,值得嘗試的狀態管理器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章