UNPKG

react-hooks-global-state-next

Version:
92 lines (80 loc) 2.85 kB
import { SetStateAction, useCallback } from 'react'; import { create } from 'zustand'; const validateStateKey = (keys: string[], stateKey: string) => { if (!keys.includes(stateKey)) { throw new Error(`'${stateKey}' not found. It must be provided in initialState as a property key.`); } }; const isFunction = (fn: unknown): fn is Function => (typeof fn === 'function'); const updateValue = <Value>(oldValue: Value, newValue: SetStateAction<Value>) => ( isFunction(newValue) ? newValue(oldValue) : newValue ); /** * Create a global state. * * It returns a set of functions * - `useGlobalState`: a custom hook works like React.useState * - `getGlobalState`: a function to get a global state by key outside React * - `setGlobalState`: a function to set a global state by key outside React * - `subscribe`: a function that subscribes to state changes * * @example * import { createGlobalState } from 'react-hooks-global-state-next'; * * const { useGlobalState } = createGlobalState({ count: 0 }); * * const Component = () => { * const [count, setCount] = useGlobalState('count'); * ... * }; */ export const createGlobalState = <State extends object>(initialState: State) => { const useStore = create<State>(() => initialState); type StateKeys = keyof State; const keys = Object.keys(initialState); const setGlobalState = <StateKey extends StateKeys>( stateKey: StateKey, update: SetStateAction<State[StateKey]>, ) => { if (process.env.NODE_ENV !== 'production') { validateStateKey(keys, stateKey as string); } useStore.setState((previousState) => ({ [stateKey]: updateValue(previousState[stateKey], update), } as Pick<State, StateKey> as Partial<State>)); }; const useGlobalState = <StateKey extends StateKeys>(stateKey: StateKey) => { if (process.env.NODE_ENV !== 'production') { validateStateKey(keys, stateKey as string); } const selector = useCallback((state: State) => state[stateKey], [stateKey]); const partialState = useStore(selector); const updater = useCallback( (u: SetStateAction<State[StateKey]>) => setGlobalState(stateKey, u), [stateKey], ); return [partialState, updater] as const; }; const getGlobalState = <StateKey extends StateKeys>(stateKey: StateKey) => { if (process.env.NODE_ENV !== 'production') { validateStateKey(keys, stateKey as string); } return useStore.getState()[stateKey]; }; const subscribe = <StateKey extends StateKeys>( stateKey: StateKey, listener: (value: State[StateKey]) => void, ) => { useStore.subscribe((state, prevState) => { if (state[stateKey] !== prevState[stateKey]) { listener(state[stateKey]); } }); }; return { useGlobalState, getGlobalState, setGlobalState, subscribe, }; };