20 行簡單實現一個 unstated-next 🎅

前言 📝

👉 unstated-next 基於 React 心智模型(hook+context)而設計的狀態管理。 👈

Alt

在 react hook 出現以前,有基於單一數據源,使用純函數修改狀態的 redux & react-redux 也有基於 Object.defineProperty 和 Proxy 來進行數據攔截訪問的 mobx ,但伴隨着 react 16.8 的出現,咱們能夠基於自帶的 hook 去實現狀態管理也就是 unstated-nextjavascript


官網 Demo 🥔

...
import { createContainer } from "unstated-next";

function useCounter(initialState = 0) {
  let [count, setCount] = useState(initialState);
  let decrement = () => setCount(count - 1);
  let increment = () => setCount(count + 1);
  return { count, decrement, increment };
}

//使用 createContainer 將 useCounter改形成提供狀態和方法的組件
let Counter = createContainer(useCounter);

function CounterDisplay() {
//從被處理過的 useCounter 中拿到狀態和方法
  let counter = Counter.useContainer();
  return (
    <div>
      <button onClick={counter.decrement}>-</button>
      <span>{counter.count}</span>
      <button onClick={counter.increment}>+</button>
    </div>
  );
}

function App() {
  return (
    <Counter.Provider>
      <CounterDisplay />
      {/* 經過initialState屬性注入初始值 */}
      <Counter.Provider initialState={2}>
            <CounterDisplay />
      </Counter.Provider>
    </Counter.Provider>
  );
}

render(<App />, document.getElementById("root"));

unstated-next 作了什麼?前端

  1. 提供 createContainer 將自定義 Hooks 封裝爲一個能夠提供狀態和方法的 數據對象
  2. 利用 useContext 構造了 Provider 注入組件獲取獲取 Store 這兩個方法

實現一個 unstated-next 🚲

import { createContext, createElement, useContext } from "react";
export default useHook => {
  const Context = createContext();
  const Provider = ({ init, children }) => {
    return createElement(Context.Provider, { value: useHook(init) }, children);
  };
  const useContainer = () => useContext(Context);
  return { Provider, useContainer };
};
  • 經過函數返回一個包含ProvideruseContainer的對象
  • Provider 接受 init 初始值,去執行 數據對象 組件,經過 createElement 創造一個 Context.Provider 傳值組件,並將 數據對象 組件返回的方法和狀態保存到value,子節點不變,返回:
<xxx.Provider value={方法,狀態...}>{children}</xxx.Provider>
  • 經過useContainer拿到 當前 Context.Provider 中的 value 狀態和方法 並返回

如何解決 Provider hell 🏁

在 unstated-next 中每個被處理爲 數據對象 的組件若是想要被共享,須要在最外層逐級包裹java

<Container1.Provider>
  <Container2.Provider>
    <Container3.Provider>MyApp</Container3.Provider>
  </Container2.Provider>
</Container1.Provider>

咱們能夠經過 相似 compose 函數進行處理,將全部 數據對象 組件經過 reduce 逐級疊加返回一個相似洋蔥的 Provider,調用的時候只須要使用Provider包裹住業務組件react

export const composeProvider = (...commonFun) => ({ children }) => {
  return commonFun.reduceRight((child, { init, Provider }) => {
    return <Provider init={init}>{child}</Provider>;
  }, children);
};

//進行調用
const Provider = reduceProvider({ ...xxxState1, init: 100 }, xxxState2);
export default () => (
  <Provider>
    <ExamplePage1 />
    <ExamplePage2 />
    <ExamplePage3 />
  </Provider>
);

查看完整代碼git

大功告成!github

總結 💢

總結redux

其實 unstated-next 實現很簡單,通俗來說就是一個閉包,使用於簡單的業務場景,且寫法過於靈活,一旦遇到 class 組件的狀況,就又要回到舊的寫法,因此只能說有利有弊微信

至此,謝謝各位在百忙之中點開這篇文章,但願對大家能有所幫助,相信你對 unstated-next 有了大概的認實,若有問題歡迎各位大佬指正。閉包

歡迎添加個人微信共同討論前端技術問題(備註:qian)ide

Alt

參考文獻

求個 star,謝謝你們了

相關文章
相關標籤/搜索