import React, { useCallback, useRef } from 'react'; import { createContext, useContextSelector } from 'use-context-selector'; type DispatchType = ( action: ActionType ) => DispatchReturn; type SelectorType = (state: StateType) => ReturnType; export const createProvider = < StateType, ActionType, DispatchReturn, ProviderProps >( body: ( props: ProviderProps ) => [state: StateType, dispatch: DispatchType] ) => { const StateContext = createContext(null as any); const DispatchContext = React.createContext< DispatchType >(null as any); const Provider: React.FC = ({ children, ...props }) => { const [state, _dispatch] = body(props as any); const dispatchRef = useRef(_dispatch); dispatchRef.current = _dispatch; // stable dispatch function const dispatch = useCallback( (action: ActionType) => dispatchRef.current?.(action), [dispatchRef] ); return ( {children} ); }; const useDispatch = () => React.useContext(DispatchContext); const useStateContext = function ( selector: SelectorType ) { return useContextSelector(StateContext, selector); }; return [Provider, useDispatch, useStateContext] as const; };