Created
November 1, 2018 18:39
-
-
Save jquense/db418c25a43bdb89ddcb43d34fccd03c to your computer and use it in GitHub Desktop.
Revisions
-
jquense created this gist
Nov 1, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,105 @@ import React, { useReducer, useContext, useMemo } from 'react' import get from 'lodash/get' import invariant from 'invariant' const INIT = { type: `INIT${Math.random() .toString(36) .substring(7) .split('') .join('.')}`, } function compose(funcs) { if (funcs.length === 0) return arg => arg if (funcs.length === 1) return funcs[0] return funcs.reduce((a, b) => (...args) => a(b(...args))) } export function bindActionCreators(actionCreators, dispatch) { if (typeof actionCreators === 'function') return (...args) => dispatch(actionCreators(...args)) const boundActionCreators = {} for (const [key, actionCreator] of Object.entries(actionCreators)) { if (typeof actionCreator === 'function') boundActionCreators[key] = (...args) => dispatch(actionCreator(...args)) } return boundActionCreators } export const combineReducers = reducers => { const entries = Object.entries(reducers) return (state = {}, action, parentPath = 'root') => { let hasChanged = false const nextState = {} for (let [key, reducer] of entries) { const path = `${parentPath}.${key}` const prevStateKey = state[key] const nextStateKey = reducer(prevStateKey, action, __DEV__ && path) invariant( nextStateKey !== undefined, action.type === INIT.type ? `Could not initialize state for reducer key: '${path}', return an initial state value or null for an empty state` : `Reducer at '${path}' did not return a state value, use null for an empty state` ) nextState[key] = nextStateKey hasChanged = hasChanged || nextStateKey !== prevStateKey } return hasChanged ? nextState : state } } const applyMiddleware = (middlewares, dispatch) => { return compose(middlewares.map(m => m(dispatch)))(dispatch) } export default (reducers, initialState = {}, middleware) => { const StateContext = React.createContext({}) const DispatchContext = React.createContext() const reducer = combineReducers(reducers) initialState = reducer(initialState, INIT) function useStoreDispatch() { return useContext(DispatchContext) } function useBoundActions(actionsToBind) { const dispatch = useStoreDispatch() return useMemo(() => bindActionCreators(actionsToBind, dispatch), [ actionsToBind, ]) } function useStoreState(key) { const state = useContext(StateContext) return key ? get(state, key) : state } function Provider({ children }) { const [state, baseDispatch] = useReducer(reducer, initialState) const dispatch = useMemo(() => applyMiddleware(middleware, baseDispatch), [ baseDispatch, ]) return ( <DispatchContext.Provider value={dispatch}> <StateContext.Provider value={state}>{children}</StateContext.Provider> </DispatchContext.Provider> ) } return { useBoundActions, useStoreDispatch, useStoreState, Provider, } }