Easy Context in React Server Components (RSC)
Tags: webdev, javascript, nextjs, state
Published: 2023-10-10 23:52:13

Continuing the need to have easy context from my other posts, I wanted to find a way to share context on the server as well.

You may have noticed that `useContext` and `createContext` do not compile on the server (another millionth reason I love SvelteKit!).

So, there needs to be a way to do this easily in RSC, even though the React Team, who seems to be extremely far away from the actual userbase, decided to depreciate this feature before it was actually implemented. 

You can find `createServerContext` in the latest React as an import, but there is no actual `Consumer` like there is in a regular context, so I couldn't get it to work. Supposedly it could be shared on the client and server, but only for small strings. The only actual mention of this is in a [tweet](https://twitter.com/sebmarkbage/status/1587615489605369861?s=20):

```js
let Lang = createServerContext("lang", "en");
...
<Lang.Provider value={…}>
...
use(Lang)
```
Either way, I would have re-written this to work like my other context, so it is a moot point.

I should also add, the second best solution is to use the [server-only-context](https://github.com/manvalls/server-only-context#readme) library (by manvalls) which uses `cache` under the hood.

___

### Dear React Team (and NextJS Team)

There is a reason there are so many external state libraries for React. The way React works out-of-the-box is terrible and uses too much boilerplate. We love RSC (the people on the React train who won't move to Svelte have no choice) and we love that Vercel wants to add Server Actions, but either of you can fix this problem. No more Context Hell please! 

React Team... Let's add signals (or something better) as well please and not worry about memoizing state! Why are you making things hard that don't have to be!!!!? Serious question.

Ok, I digress.
___

### The Solution

So I basically copied the `cache` idea from above, but simplified it for my use case with the Map from my other solutions. The `cache` is actually a Map itself, so I believe this is a Map of a Map under the hood.

#### use-server-provider.tsx
```ts
import 'server-only';
import { cache } from 'react';

const serverContext = cache(() => new Map());

export const useServerProvider = <T,>(
    key: string,
    defaultValue?: T
) => {
    const global = serverContext();

    if (defaultValue !== undefined) {
        global.set(key, defaultValue);
    }

    return [
        global.get(key),
        (value: T) => global.set(key, value)
    ];
};
```
#### Parent
```ts
const [count, setCount] = useServerProvider('count', 23);
```

#### Child
```ts
const [count] = useServerProvider<number>('count');
```

However, it is worth noting, unlike a reactive component, if you set the component after you define the `count` variable, it won't update unless you grab the `count` variable again. One way to fix this is to use a `value` object key like in a signal:

```ts
import 'server-only';
import { cache } from 'react';

const serverContext = cache(() => new Map());

export const useServerProvider = <T,>(
    key: string,
    defaultValue?: T
) => {
    const global = serverContext();

    if (defaultValue !== undefined) {
        global.set(key, defaultValue);
    }

    return {
        get value() {
            return global.get(key)
        },
        set value(v: T) {
            global.set(key, v);
        }
    }
};
```
Then you could do this:

#### Parent
```ts
const count = useServerProvider('count', 23);

count.value = 27;
```

#### Child
```ts
export default function TestChild() {

    const count = useServerProvider<number>('count');

    return (
        <p>Child: {count.value}</p>
    )
}
```
Nevertheless, on a server you shouldn't care about any of this... so the original react-like way still stands.

- [GitHub Repo](https://github.com/jdgamble555/server-context)
- [CodeSandBox](https://codesandbox.io/p/github/jdgamble555/server-context/master?file=%2Fapp%2Fuse-server-provider.tsx%3A1%2C1)

Probably the last article in this series... probably...

J

Getting back to rebuilding [code.build](https://code.build)