Skip to content

Instantly share code, notes, and snippets.

@kentcdodds
Last active September 24, 2021 08:42
Show Gist options
  • Save kentcdodds/0c3349caf28c53ee383841a02580c9ac to your computer and use it in GitHub Desktop.
Save kentcdodds/0c3349caf28c53ee383841a02580c9ac to your computer and use it in GitHub Desktop.
A simple context creator that doesn't allow any logic. It's 100% just to make it easy to pass a value from a parent to all children, specifically useful for remix where you just want a loader's data to be accessible anywhere else in your tree.
function createSimpleContext<ContextType>(name: string) {
const defaultValue = Symbol(`Default ${name} context value`)
const Context =
React.createContext<ContextType | null | typeof defaultValue>(defaultValue)
Context.displayName = name
function useValue() {
const user = React.useContext(Context)
if (user === defaultValue) {
throw new Error(`use${name} must be used within ${name}Provider`)
}
if (!user) {
throw new Error(
`No value in ${name}Provider context. If the value is optional in this situation, try useOptional${name} instead of use${name}`,
)
}
return user
}
function useOptionalValue() {
const user = React.useContext(Context)
if (user === defaultValue) {
throw new Error(`useOptional${name} must be used within ${name}Provider`)
}
return user
}
return {Provider: Context.Provider, useValue, useOptionalValue}
}
const {
Provider: UserProvider,
useValue: useUser,
useOptionalValue: useOptionalUser,
} = createSimpleContext<User>('User')
// then elsewhere:
export default function AppWithProviders() {
const data = useRouteData<LoaderData>()
return (
<UserProvider value={data.user}>
{/* ... */}
</UserProvider>
)
}
// and then in a child route somewhere:
export default function RequiresAUser() {
const user = useUser()
// ...
}
// and in another one where the user is optional:
function UserIsOptional() {
const user = useOptionalUser()
// ...
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment