import { createContext, Provider, useContext } from 'react' /** * A hook that retrieves a value of a certain context when called. * In addition to that it carries a `Provider` component that can be used to override the context's value. * * @typeParam T type of the value kept in the context * @see [[createUseContext]] */ export type UseContext = (() => T) & { /** * The internal `MyContext.Provider` component published through the hook itself so that these two form an inseparable couple. */ Provider: Provider } /** * Creates a `useContext` hook for a newly allocated context with the specified name. * * **Example:** * ```jsx * const useMyContext = createUseContext({contextName: 'MyContext'}) * * const MyComponent = () => { * const myValue = useMyContext() * } * * const MyApp = () => ( * * ... * ({ contextName }: { contextName: string }): UseContext /** * Creates a `useContext` hook for a newly allocated context with the specified name and a given default value. * * **Example:** * ```jsx * const useMyContext = createUseContext({contextName: 'MyContext', defaultValue: ... }) * * const MyComponent = () => { * const myValue = useMyContext() * } * * const MyApp = () => ( * * ... * ({ contextName, defaultValue }: { contextName: string, defaultValue: T }): UseContext /** * Creates a `useContext` hook for a newly allocated context with the specified name. * * Invocation of the hook will fail if there is no value provided through the `Provider` component. * * **Example:** * ```jsx * const useMyContext = createUseContext({contextName: 'MyContext', nonNull: true, nullMessage: 'Value not available, did you use ?' }) * * const MyComponent = () => { * const myValue = useMyContext() * } * * const MyApp = () => ( * * ... * ({ contextName, nonNull, nullMessage }: { contextName: string, nonNull: true, nullMessage?: string }): UseContext export function createUseContext ({ contextName, defaultValue, nonNull = false, nullMessage }: { contextName: string, defaultValue?: T, nonNull?: boolean, nullMessage?: string }) { const context = createContext(defaultValue) context.displayName = contextName const useContextValue = (() => { const value = useContext(context) if (nonNull && value == null) { throw new Error(nullMessage ?? `value expected for context '${contextName}', got '${value}'`) } return value }) as UseContext useContextValue.Provider = context.Provider return useContextValue }