If you've ever used Svelte, and you know how easy the getContext
and setContext
apis are, then you might get a little jealous when you go back to React.
I'm not a huge fan of React, but I figured I would help some of you who get into React Context Hell. Some things get easier thanks to NextJS, but I don't see React working too hard to fix some of the classic problems like lack of signals and context hell.
So, here is a fix. How about one global provider that is just easy?
Installation
This is the core hook.
use-provider.tsx
'use client'
import { FC, ReactNode, createContext, useContext } from "react";
const _Map = <T,>() => new Map<string, T>();
const Context = createContext(_Map());
export const Provider: FC<{ children: ReactNode }> = ({ children }) =>
<Context.Provider value={_Map()}>{children}</Context.Provider>;
export const useProvider = <T,>(key: string) => {
const context = useContext(Context);
return {
set value(v: T) { context.set(key, v); },
get value() {
if (!context.has(key)) {
throw Error(`Context key '${key}' Not Found!`);
}
return context.get(key) as T;
}
}
};
Import and add <Provider />
to your home component:
page.tsx
'use client';
import Test from "./test";
import { Provider } from "./use-provider";
export default function Home() {
return (
<Provider>
<main>
<p>Parent</p>
<Test />
</main>
</Provider>
);
}
Usage
Import useProvider
just like any other hook. Pass in the key you want to use for your object. Your value can be anything and any type. It will return get
and set
functions. Use them wherever you like in your app!
test.tsx
'use client'
import TestChild from "./test-child";
import { useProvider } from "./use-provider";
export default function Test() {
const user = useProvider<string>('user');
user.value = 'Jonathan';
return (
<>
<TestChild />
</>
)
}
test-child.tsx
'use client'
import { useProvider } from "./use-provider"
export default function TestChild() {
const user = useProvider<string>('user');
return (
<>
<p>{user.value}</p>
</>
)
}
You can use as many providers as you like, just like you would with context. However, if you need more than one your probably really just need to pass / share an object instead.
Note: - This does not mean the values will be reactive. You need to use useState
or useReducer
concurrently with useProvider
just like you normally would with useContext
.
Sure, you could create a complex reactive store on top of this (which I may write up later), but the point of this post is to make sharing context... well, easy.
If you like this pattern, see:
J