3

20 行简单实现一个 unstated-next 🎅

 3 years ago
source link: https://segmentfault.com/a/1190000039854954
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

👉 unstated-next 基于 React 心智模型(hook+context)而设计的状态管理。 👈

在 react hook 出现之前,有基于单一数据源,使用纯函数修改状态的 redux & react-redux 也有基于 Object.defineProperty 和 Proxy 来进行数据拦截访问的 mobx ,但伴随着 react 16.8 的出现,我们可以基于自带的 hook 去实现状态管理也就是 unstated-next


官网 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 中每一个被处理为 数据对象 的组件如果想要被共享,需要在最外层逐级包裹

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

我们可以通过 类似 compose 函数进行处理,将所有 数据对象 组件通过 reduce 逐级叠加返回一个类似洋葱的 Provider,调用的时候只需要使用Provider包裹住业务组件

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

查看完整代码

大功告成!

其实 unstated-next 实现很简单,通俗来讲就是一个闭包,使用于简单的业务场景,且写法过于灵活,一旦遇到 class 组件的情况,就又要回到旧的写法,所以只能说有利有弊

至此,谢谢各位在百忙之中点开这篇文章,希望对你们能有所帮助,相信你对 unstated-next 有了大概的认实,如有问题欢迎各位大佬指正。

欢迎添加我的微信共同讨论前端技术问题(备注:qian)

Alt

求个 star,谢谢大家了


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK