import { type Context, type Provider, createContext, useContext } from 'react' import * as selector from 'use-context-selector' const createCreateCtxFunction = ( useContextImpl: typeof useContext, createContextImpl: typeof createContext) => { return function({ name, defaultValue }: CreateCtxOptions = {}): CreateCtxReturn { const emptySymbol = Symbol(`empty ${name}`) // @ts-expect-error it's ok here const context = createContextImpl(defaultValue ?? emptySymbol) const useContextValue = () => { const ctx = useContextImpl(context) if (ctx === emptySymbol) throw new Error(`No ${name ?? 'related'} context found.`) return ctx } const result = [context.Provider, useContextValue, context] as CreateCtxReturn result.context = context result.provider = context.Provider result.useContextValue = useContextValue return result } } type CreateCtxOptions = { defaultValue?: T name?: string } type CreateCtxReturn = [Provider, () => T, Context] & { context: Context provider: Provider useContextValue: () => T } // example // const [AppProvider, useApp, AppContext] = createCtx() export const createCtx = createCreateCtxFunction(useContext, createContext) export const createSelectorCtx = createCreateCtxFunction( selector.useContext, selector.createContext as typeof createContext, )