Skip to content

Instantly share code, notes, and snippets.

@raphtlw
Last active July 30, 2022 12:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raphtlw/f4a7c3ebdd622f562139400a66916e4c to your computer and use it in GitHub Desktop.
Save raphtlw/f4a7c3ebdd622f562139400a66916e4c to your computer and use it in GitHub Desktop.
Inline functional context builder
import {
useContext,
createContext,
type ReactNode,
type Provider as ReactProvider,
} from 'react'
/**
* Utility context builder which streamlines the context
* building and exposing API.
*
* @param displayName
* Display name which is shown in React DevTools
* (default: 'Context')
* @param ProviderWrapper
* Context's provider wrapper which provides the context provider
*
* @returns [Context's provider (Provider),
* Context's consumer (useContext),
* Utility HOC (withProvider)]
*/
export const buildContext = <
TContext extends {},
ProviderWrapperProps extends {}
>(
displayName: string = 'Context',
ProviderWrapper: React.FC<
{
children: ReactNode
Root: ReactProvider<TContext | null>
} & ProviderWrapperProps
>
) => {
const Context = createContext<TContext | null>(null)
Context.displayName = displayName
const Consumer = (): TContext => {
const context = useContext(Context)
if (!context) {
throw new Error(
`use${displayName} can only be used inside ${displayName}Provider`
)
}
return context
}
const Provider: React.FC<{ children: ReactNode } & ProviderWrapperProps> = ({
children,
...props
}) => (
<ProviderWrapper Root={Context.Provider} {...props}>
{children}
</ProviderWrapper>
)
const withProvider = (props: ProviderWrapperProps) => {
const providerWrapper = (Component: React.FC) => (
<Provider {...props}>
<Component />
</Provider>
)
return providerWrapper
}
return [Provider, Consumer, withProvider] as const
}
@raphtlw
Copy link
Author

raphtlw commented Jul 29, 2022

Usage

const [UserProvider, useUser] = buildContext<UserStore, { name: string }>(
  'User',
  ({ Root, children, name }) => {
    const [user, setUser] = useState<User | null>(null)

    console.log(name)

    return <Root value={{ user, setUser }}>{children}</Root>
  }
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment