[TypeScript/React Hooks] useReducer + useContextで死ぬほど簡単にグローバルなストアを作る


import React, {
    createContext,
    FC,
    useContext,
    useEffect,
    useReducer,
} from 'react'

type NewStateAction = Partial<S> | ((prevState: S) => Partial<S>)
type ContextValue = {
    globalState: S
    setGlobalState: (newState: NewStateAction) => void
}

type S = typeof initialState

const initialState = {
    count: 0,
}

export const Store = createContext({} as ContextValue)

export const Provider: FC<{}> = ({ children }) => {
    const [globalState, setGlobalState] = useReducer(
        (prev: S, newState: NewStateAction) => {
            const _newState =
                typeof newState === 'function' ? newState(prev) : newState
            return { ...prev, ..._newState }
        },
        initialState,
    )

    useEffect(() => {
        // count が変更された場合の処理
    }, [globalState.count])

    return (
        <Store.Provider value={{ globalState, setGlobalState }}>
            {children}
        </Store.Provider>
    )
}

おわり。勢いで書いたのでどっか間違ってるかもしれません。

コンポーネントでは以下のように使います。

const Component = props => {
    const { globalState, setGlobalState } = useContext(Store)

    globalState.count // get

    setGlobalState({ count: 1 }) // set
    setGlobalState(({ count }) => ({ count: count + 1 })) // set
}

const App = () => (
    <Provider>
        <Component></Component>
    </Provider>
)

useEffect など通常の React Hooks の API が使えるので統一感があっていいですね!!