Created
November 1, 2018 18:39
-
-
Save jquense/db418c25a43bdb89ddcb43d34fccd03c to your computer and use it in GitHub Desktop.
redux react hooks
This file contains 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 characters
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, | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment