UNPKG

react-sweet-state

Version:

Global + local state combining the best of Redux and Context API

56 lines (53 loc) 1.99 kB
import { useMemo, useContext, useRef, useCallback, useState } from 'react'; import { useSyncExternalStore } from 'use-sync-external-store/shim'; import { Context } from '../context'; import { getSelectorInstance } from '../utils/create-selector'; const EMPTY_SELECTOR = () => undefined; const DEFAULT_SELECTOR = state => state; export function createHook(Store, _temp) { let { selector } = _temp === void 0 ? {} : _temp; return function useSweetState(propsArg) { const { retrieveStore } = useContext(Context); const { storeState, actions } = retrieveStore(Store); const hasPropsArg = propsArg !== undefined; const propsArgRef = useRef(propsArg); propsArgRef.current = propsArg; const stateSelector = useMemo(() => selector ? getSelectorInstance(selector, storeState, hasPropsArg) : selector === null ? EMPTY_SELECTOR : DEFAULT_SELECTOR, [hasPropsArg, storeState]); const forceUpdate = useState({})[1]; const getSnapshot = useCallback(() => { // parent scope has changed and notify was explicitly triggered by the container // we need to force the hook to re-render to listen new storeState if (retrieveStore(Store).storeState !== storeState) forceUpdate({}); const state = storeState.getState(); return stateSelector(state, propsArgRef.current); }, [retrieveStore, storeState, stateSelector, forceUpdate]); const currentState = useSyncExternalStore(storeState.subscribe, getSnapshot, getSnapshot); return [currentState, actions]; }; } export function createActionsHook(Store) { const useHook = createHook(Store, { selector: null }); return function useSweetStateActions() { return useHook()[1]; }; } export function createStateHook(Store, _temp2) { let { selector } = _temp2 === void 0 ? {} : _temp2; const useHook = createHook(Store, { selector }); return function useSweetStateState(propsArg) { return useHook(propsArg)[0]; }; }