Freakin Easy React State Management with useProvider Hook
Tags: react, reacthooks, nextjs, webdev
Published: 2023-05-29 22:29:00

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](https://dev.to/alfredosalzillo/the-react-context-hell-7p4). 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
```ts
'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
```ts
'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
```ts
'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
```ts
'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:
- Qwik [useShared](https://code.build/p/RUGSxMmSnVizh6UTBoSJ17/building-a-custom-menu-hook-with-qwik)
- Svelte [useShared](https://dev.to/jdgamble555/the-correct-way-to-use-stores-in-sveltekit-3h6i)



J