context.ts 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445
  1. import { type Context, type Provider, createContext, useContext } from 'react'
  2. import * as selector from 'use-context-selector'
  3. const createCreateCtxFunction = (
  4. useContextImpl: typeof useContext,
  5. createContextImpl: typeof createContext) => {
  6. return function<T>({ name, defaultValue }: CreateCtxOptions<T> = {}): CreateCtxReturn<T> {
  7. const emptySymbol = Symbol(`empty ${name}`)
  8. // @ts-expect-error it's ok here
  9. const context = createContextImpl<T>(defaultValue ?? emptySymbol)
  10. const useContextValue = () => {
  11. const ctx = useContextImpl(context)
  12. if (ctx === emptySymbol)
  13. throw new Error(`No ${name ?? 'related'} context found.`)
  14. return ctx
  15. }
  16. const result = [context.Provider, useContextValue, context] as CreateCtxReturn<T>
  17. result.context = context
  18. result.provider = context.Provider
  19. result.useContextValue = useContextValue
  20. return result
  21. }
  22. }
  23. type CreateCtxOptions<T> = {
  24. defaultValue?: T
  25. name?: string
  26. }
  27. type CreateCtxReturn<T> = [Provider<T>, () => T, Context<T>] & {
  28. context: Context<T>
  29. provider: Provider<T>
  30. useContextValue: () => T
  31. }
  32. // example
  33. // const [AppProvider, useApp, AppContext] = createCtx<AppContextValue>()
  34. export const createCtx = createCreateCtxFunction(useContext, createContext)
  35. export const createSelectorCtx = createCreateCtxFunction(
  36. selector.useContext,
  37. selector.createContext as typeof createContext,
  38. )