内容简介:本文需要先行了解Hooks 的基础知识。React 状态管理实现有两种,一种是Flux 架构的,例如Redux,其通过 Context 实现全局状态共享;另一种是响应式的,例如Mobx,通过可观测对象和 HOC 实现状态共享。在 Hooks 出来后,之前的通过 props 的状态解决方案就有些过于繁琐了,鉴于之前的状态管理的复杂,前两天我写了一个状态管理工具Piex Store,完全面向对象,基于 Hooks,不借助于 Context 实现状态共享。
本文需要先行了解Hooks 的基础知识。
React 状态管理实现有两种,一种是Flux 架构的,例如Redux,其通过 Context 实现全局状态共享;另一种是响应式的,例如Mobx,通过可观测对象和 HOC 实现状态共享。
在 Hooks 出来后,之前的通过 props 的状态解决方案就有些过于繁琐了,鉴于之前的状态管理的复杂,前两天我写了一个状态管理工具Piex Store,完全面向对象,基于 Hooks,不借助于 Context 实现状态共享。
本文将基于其核心原理,逐步实现一个最简单的状态管理工具,其核心代码只有 13 行。
Custom Hook
Hooks API 出来后,Function Component(函数组件,以下简称 FC) 也有了自己的状态,而且可以自定义Custom Hook,这便让我们对组件状态有个更多的操作可能性。以下是一个简单的自定义 Hooks:
const useCounter = () => { const [count, setCount] = useState(0); const increment = useCallback(() => { setCount(count + 1); }, [count]); const decrement = useCallback(() => { setCount(count - 1); }, [count]); return {count, increment, decrement}; } 复制代码
如果要使用的话需要在一个 FC 里:
const Counter = () => { const {count, increment, decrement} = useCounter(); return ( <article> <p>{count}</p> <button onClick={increment}>Increment</button> <button onClick={decrement}>Decrement</button> </article> ) } 复制代码
这样我们就实现了一个简单的 custom hooks,可以复用对 count
的操作逻辑。
但是这里有一个问题,大家可以运行这个例子,我们使用两个 Counter
组件的话,它们的 count
是互不关联的,两者之间没有任何关系。如果我们有办法让 count
共享,每次修改状态就能在所有用到的地方同步到状态,不就是状态共享吗!
控制组件更新
共享状态的一个问题就是如何把状态同步到所有组件并更新页面,其实通过 useState
就可以轻松实现,下面我们看一个例子:
const App = () => { const [,setState] = useState(Math.random()); setTimeout(()=>{ setState(Math.random()); }, 200); return ( <p>{Date.now()}</p> ) }; 复制代码
点击这里 查看实际运行效果。
可以发现这里没有取 useState
的第一个参数,而只是用了第二个参数更新状态,实际上每 200ms 后页面上就显示不同的时间戳。
其实 useState
并不是什么黑魔法,具体实现原理可以看我的这篇文章。简单来讲,就是我们每次 setState
时,如果参数和前一个 state
不相等,React 就会重新运行函数组件,把返回的值做 DOM Diff 来更新页面,所以关键就在于 setState
,如果我们掌握了 setState,就掌握了更新组件的时机。
状态共享
我们通过上面的例子知道 useState
返回数组的第二个值,这里称为 setState
,可以控制组件的渲染。
那么如果有一个方法,把所有用到共享状态的组件都创建一个 useState
,并把第二个参数存储起来,每次更新共享状态时,把所有的 setState
都运行一遍,那么不就可以更新所有组件的状态了吗?不就实现状态共享了吗?
下面我们通过一个简单的例子看一下:
let _count = 0; let _setters = []; const useCounter = () => { const [, setCount] = useState(_count); _setters.push(setCount); const increment = useCallback(() => { _count++; _setters.forEach(setState => setState(_count)); }, [_count]); const decrement = useCallback(() => { _count--; _setters.forEach(setState => setState(_count)); }, [_count]); return {count:_count, increment, decrement}; } const Counter = () => { const {count, increment, decrement} = useCounter(); return ( <article> <p>{count}</p> <button onClick={increment}>Increment</button> <button onClick={decrement}>Decrement</button> </article> ) } const App = () => { return ( <div> <Counter /> <Counter /> </div> ) } 复制代码
运行效果点击这里 查看。
这个例子是基于第一个 custom hook 的例子改动的,可以发现点击一个按钮,页面上两个 Counter
组件的 count
值都变了。
这是因为我把 useCounter
的状态存到全局的 _count
变量中了,并且把 useState
的第二个参数也都收集到全局的 _setters
数组中了,每次操作 increment
或者 decrement
时,就会先改变 _count
的值,然后触发 setters
中的 setState
,这样所有 Counter
组件都会更新啦。而且返回的是 _count
变量重命名为 count
,所以每个组件的 setState
被触发后都会通过 _count
得到最新的值并显示在页面上。
这样我们就实现了一个最简单的计数器状态共享,但是每次都自己写太麻烦了,可以设计一个简单易用,立马可以上手的通用 工具 使用。
通用工具
设计一个通用工具需要看应用场景和通用模型,面向对象是一个不错的选择。关于更多细节可以看Piex Store 核心概念 来了解,我们看一下怎么实现:
export abstract class Store { state = {}; setters = []; setState(newState) { this.state = newState; this.setters.forEach(setState => setState(this.state)); } } export function useStore(store) { let [, setState] = useState(store.state); store.setters.push(setState); return store; } 复制代码
由于 JS 不支持继承,所以这段代码用 TS 实现,去掉空行,短短 13 行代码就实现了一个状态管理:
-
state
对应上例中的_count
,存储全局状态; -
setters
对应上例中的_setters
,收集setState
; -
setState
方法用来更新组件; -
useStore
则用来收集依赖;
具体怎么使用呢?如下:
class CounterStore extends Store { state = { count: 0, } increment() { this.setState({ count: this.state.count + 1, }) } decrement() { this.setState({ count: this.state.count + 1, }) } } const counterStore = new CounterStore(); const Counter = () => { const store = useStore(counterStore); return ( <article> <p>{store.state.count}</p> <button onClick={store.increment}>Increment</button> <button onClick={store.decrement}>Decrement</button> </article> ) } const App = () => { return ( <div> <Counter /> <Counter /> </div> ) } 复制代码
这样,所有用到 counterStore
的地方都可以共享 counterStore.state
的变量,还可以通过对象方法来更新 state
并同步到其它组件。
最后
当然,这只是一个简单的 demo,很多东西都没有做,如 setters
收集的 setState
依赖在组件卸载时释放;全量更新状态太麻烦,仅部分更新就可以了,还有数据变更检测等等。
这些肯定不是 13 行代码可以实现的了,如果感兴趣可以看Piex Store,基于上述原理实现的状态管理工具,完美支持 TS 类型推断,遵从 React 设计哲学,支持中间件,可以使用 Redux DevTools 观察状态变更。
由于 Piex Store 还处于襁褓状态,很多地方还有需要完善的地方,还请大家多多支持。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- javascript – 有可能使用angularJs将状态从状态传递到状态吗?
- 前端状态管理与有限状态机
- 如何可视化需求状态和团队状态?
- 版本管理工具及 Ruby 工具链环境
- 给 DSL 开个脑洞:无状态的状态机
- 密码管理工具(命令行)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
付费:互联网知识经济的兴起
方军 / 机械工业出版社 / 2017-6-1 / CNY 59.00
关于互联网知识付费的首部作品 知识工作正在被重塑,知识经济正在开启互联网时代下半场 为你展现互联网知识经济全景大图,解读新物种的前世今生 内容简介 一个产业解读 三个分析工具 一组知识卡片 书是最早的知识载体,已有2000多年的付费历史,随着移动互联网的普及,新的知识经 济在今天爆发,知识的创造者和传播者从书后走到了书前,互联网知识经济正在拉开帷幕。知识的......一起来看看 《付费:互联网知识经济的兴起》 这本书的介绍吧!