本文旨在用尽量通俗易懂的方式讲清楚 hox 在底层是如何实现的,也所以可能在某些方面的表述不够严谨,若有偏颇,还请各位读者多多指正。
Hox 是下一代的 React 状态管理器,关于它的更多介绍能够参考这篇文章,本文假设你已经对 hox 的用法有了初步的了解。git
在 hox 中,咱们但愿能充分发挥 React Hooks 的特性,也但愿让全局状态和组件内部状态的编写体验保持一致,因此咱们使用 custom Hook 的方式定义 model 的逻辑。github
咱们不妨先来看下一个普通的 custom Hook 是什么样的效果吧:segmentfault
export function useFoo() { const [count, setCount] = useState(0) function increment() { setCount(count + 1) } return { count, increment, } }
咱们能够在组件中调用 useFoo
:app
export function ComponentA() { const foo = useFoo() return ( <div> <p>{foo.count}</p> <button onClick={foo.increment}>Increment</button> </div> ) }
不难发现,咱们经过定义 custom Hook ,对逻辑进行了一层封装,也让组件变得更加简洁。然而,若是此时咱们再建立一个 B 组件:spa
export function ComponentB() { const foo = useFoo() return ( <div> <p>{foo.count}</p> </div> ) }
当咱们点击 A 组件中的 Increment 按钮时,B 组件中的数字会跟着变化么?
答案天然是不会的,由于 useFoo
这个 custom Hook 虽然实现了逻辑的封装和复用,可是却并不能让数据共享。code
而 hox ,就是为了解决这个问题而生。cdn
咱们不妨接着上面的例子,使用 hox 提供的 createModel
对 useFoo
进行一层封装:blog
const useFooModel = createModel(useFoo)
createModel
会建立一个 Executor
组件的实例,并在其中执行 useFoo
这个 Hook ,并把 useFoo
的执行结果保存起来。最后,它会返回一个新的 Hook: useFooModel
。乍一看, createModel
和 HOC (高阶组件)甚至有几分神似。jsx
不一样于 useFoo
,在调用 useFooModel
时,咱们并非真的执行了 useFoo
这个 custom Hook ,而是向 Executor
组件进行数据的订阅。也就是说,若是咱们在多个组件中都调用了 useFooModel
,那它们所拿到的数据其实是同一份,更准确的说,它们拿到的那份数据,就是存在 Executor
中的那份 state
。rem
经过这种方式,咱们让 custom Hook 能够作到数据的共享,这也是 createModel
之因此叫作 createModel
的缘由:经过它,咱们把一个 custom Hook 变成了一个全局的 model 。
如今,咱们再来更新一下刚才的例子:
export function ComponentA() { const foo = useFooModel() // 这里从 useFoo 变成了 useFooModel return ( <div> <p>{foo.count}</p> <button onClick={foo.increment}>Increment</button> </div> ) } export function ComponentB() { const foo = useFooModel() // 这里从 useFoo 变成了 useFooModel return ( <div> <p>{foo.count}</p> </div> ) }
如今,A B 两个组件都获取到了 Executor
中的那份数据,而且订阅了它将来的更新。所以当咱们点击组件 A 的 Increment 按钮时,咱们其实是触发了 Executor
组件中的一个 setState
,而后 Executor
组件进行重渲染,通知它的订阅者们。A 组件和 B 组件收到了更新通知和新的数据,也会跟着从新进行渲染。最终咱们能够看到,A B 两个组件显示的数字都变成了 1
。