20 行简单实现一个 unstated-next 🎅
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 做了什么?
- 提供 createContainer 将自定义 Hooks 封装为一个可以提供状态和方法的 数据对象
- 利用 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 };
};
- 通过函数返回一个包含
Provider
和useContainer
的对象 - 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)
求个 star,谢谢大家了
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK