關於react hook我就很少介紹了。hook提供了抽象狀態的能力,天然而然讓人想到能夠基於hook抽離全局狀態。其天生自帶輪子光環,因此社區也出現了很多基於hook的狀態管理工具,好比說前陣子飛冰團隊出的icestore,亦或者這個stamen,不過相對來講我更喜歡的仍是這個unstated-next。javascript
那既然別人都已經造了那麼多輪子了,爲何本身還要造呢?天然是由於:vue
好比說unstated-next,它本質上是把一個自定義hook全局化了。理念很好,惋惜顆粒度太大了一點。必須把state、actions、effects維持在一個自定義hook中。內部的一系列actions、effects須要加useCallback、useMemo也比較麻煩,若是抽離到外部,又要傳不少參數,寫TS的話,還要寫很多泛型。總之,若是項目相對比較複雜,寫起來比較累。java
stamen 其實也不錯。聲明一個store,包含state、reducer、effects。並且不須要給組件包裹Provider,各個地方隨意拔插,響應更新。就是dispatch我不太喜歡用,不太好直接定位到action或effect的聲明,且丟了入參出參類型。react
icestore 的問題也差很少。說是支持TS,實際上是殘缺的,看了下源碼,類型徹底都丟失了。另外命名空間這一套我也不是很喜歡。git
固然上述這些問題人家也能優化。可是何須呢,原本也沒幾行代碼,給人家提PR的時間,我本身都寫好輪子了。因此總而言之,仍是本身造吧。github
那我本身想要的狀態管理工具是怎麼樣的呢?在hoox以前呢,其實我還實現了一版,基本複製dva的api的一個版本(把 yield 換成 async/await )。有點兒像icestore,只不過沒有命名空間。最致命且沒法解決的問題就是丟失了類型,丟失了函數引用。api
後來我總結了一下,我真正想要的是怎麼樣的:async
因此目標很簡單,能夠說就是 unstated-next 的去hook包裹版。因而我實現了一版,最終效果以下:ide
// store.js
import createHoox from 'hooxjs'
// 聲明全局初始狀態
const state = {
count: 1
}
// 建立store
export const { setHoox, getHoox, useHoox } = createHoox(state)
// 建立一個 action
export const up = () => setHoox(({ count }) => ({ count: count + 1 }))
// 建立一個effect
export const effectUp = () => {
const [{ count }, setHoox] = getHoox();
const newState = { count: count + 1 }
return fetch('/api/up', newState).then(() => setHoox(newState))
// 或者直接引用action
// return fetch('/api/up', newState).then(up)
}
複製代碼
import { useHoox, up, effectUp } from './store';
function Counter() {
const [state] = useHoox()
return (
<div> <div>{state.count}</div> <button onClick={up}>up</button> <button onClick={effectUp}>effectUp</button> </div>
)
}
複製代碼
import { useHoox } from './store';
function Counter() {
const [state, setHoox] = useHoox()
return (
<div> <div>{state.count}</div> <input value={state.count} onChange={value => setHoox({ count: value })} / </div> ) } 複製代碼
咱們知道,在class組件中,經過 this.setState
是作狀態的合併更新。可是在function組件中, useState
返回的第二個參數 setState
又是作替換更新。實際使用中,其實咱們都有訴求。尤爲是非TS的項目,狀態模型多是動態的,極可能須要作重置狀態。爲了知足全部人的需求,我也加了個api方便你們使用函數
import { useHoox } from './store';
function Counter() {
const [state, setHoox, resetHoox] = useHoox()
return (
<div> {state ? <div>{state.count}</div> : null} <button onClick={() => resetHoox(null)>reset</button> </div>
)
}
複製代碼
經過上述api,其實咱們還能夠實現相似vue中 computed
的效果。
import { useHoox } from './store';
export function useDoubleCount () {
const [{ count }] = useHoox();
return count * 2
}
複製代碼
對於某些很是複雜的運算,咱們也可使用 react 的 useMemo
作優化。
import { useHoox } from './store';
export function usePowCount (number = 2) {
const [{ count }] = useHoox();
return useMemo(() => Math.pow(count, number), [count, number])
}
複製代碼
除此外,也能夠實現一些全局effect。
hoox底層基於 context
跟 useState
實現,因爲把狀態存在 context
了中,故而相似Redux,消費狀態的組件必須是相應 Context.Provider
的子孫組件。如:
import { Provider } from './store';
import Counter from './counter';
function App() {
return <Provider> <Counter /> </Provider>
}
複製代碼
這進而致使了,若是一個組件須要消費兩個store,那就須要成爲兩個 Provider
的子孫組件。
hoox提供了一個語法糖 createContainer
,能夠稍微的簡化一下語法。
import { createContainer } from './store';
import Counter from './counter';
function App () {
return <Counter /> } export default createContainer(App) 複製代碼
留給評論區
具體的源碼跟api介紹能夠見github:github.com/wuomzfx/hoo…
關於源碼部分我就不詳細說明啦,也沒幾行代碼,看看就能明白。