Skip to content

Commit e979fca

Browse files
committed
Update decision doc with context deets
1 parent c47c648 commit e979fca

File tree

1 file changed

+29
-3
lines changed

1 file changed

+29
-3
lines changed

decisions/0014-context-middleware.md

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,13 @@ We've done a lot of work since then to get us to a place where we could ship a m
2424

2525
### Leverage a new type-safe `context` API
2626

27-
We originally considered leaning on our existing `context` value we pass to server-side `loader` and `action` functions, and implementing a similar client-side equivalent for parity. However, the type story around `AppLoadContext` isn't great, so that would mean implementing a new API client side that we knew we weren't happy with from day one. And then likely replacing it with a better version fairly soon after.
27+
We originally considered leaning on our existing `context` (`type AppLoadContext`) value we pass to server-side `loader` and `action` functions as the `context` for middleware functions. Using this would make for an easier adoption of middleware for apps that use `AppLoadContext` today. However, there were a few downsides to that approach.
2828

29-
Instead, when the flag is enabled, we'll be replacing `AppLoadContext` with a new type-safe `context` API that is similar in usage to the `React.createContext` API:
29+
First, the type story is lacking because it's just a global interface you augment via declaration merging so it's not true type safety and is more of a "trust me on this" scenario. We've always known it wasn't a great typed API and have always assumed we'd enhance it at some point via a breaking change behind a future flag. The introduction of middleware should result in much _more_ usage of `context` than exists today since it'll open up to user of `react-router-serve` as well, so it made more sense to ship the breaking change flag now for the smaller surface area of `context`-enabled apps users, ionstead of later for a much larger surface area of apps.
30+
31+
Second, in order to implement client-side middleware, we need to introduce a new `context` concept on the client - and we would like that to be the same API as we have on the server. So, if we chose to stick with `AppLoadContext`, we'd then have to implement a brand new `ClientAppLoadContext` which would suffer the same type issues out of the gate. So it felt lazy to ship a known-subpar-API to the client. Furthermore, even iof we did ship it - we'd _still_ want to enhance it later - so we'd be shipping a mediocre client `context` API _knowing_ that we would be breaking shortly after with a better typed API.
32+
33+
So, we decided to rip the band-aid off and include the breaking `context` change with the initial release of middleware. When the flag is enabled, we'll be replacing `AppLoadContext` with a new type-safe `context` API that is similar in usage to the `React.createContext` API:
3034

3135
```ts
3236
let userContext = unstable_createContext<User>();
@@ -48,11 +52,33 @@ export async function loader({ context }: Route.LoaderArgs) {
4852
}
4953
```
5054

55+
If you have an app already using `AppLoadContext`, you don't need to split that out and can just stick that object into it's own context value and maintain the same shape:
56+
57+
```diff
58+
+ let appContext = unstable_createContext<AppLoadContext>()
59+
60+
function getLoadContext(req, res) {
61+
let appLoadContext = { /* your existing object */ };
62+
63+
- return appLoadContext
64+
+ return new Map([[appContext, appLoadContext]]);
65+
}
66+
67+
function loader({ context }) {
68+
- context.foo.something();
69+
+ // Hopefully this can be done via find/replace or a codemod
70+
+ context.get(appContext).foo.something()
71+
// ...
72+
}
73+
```
74+
5175
#### Client Side Context
5276

53-
In order to support the same API on the client, we will also add support for a client-side `context` value which is already a [long requested feature][client-context]. If you need to provide initial values (similar to `getLoadContext` on the server), you can do so with a new `getContext` method which returns a `Map<RouterContext, unknown>`:
77+
In order to support the same API on the client, we will also add support for a client-side `context` of the same type (which is already a [long requested feature][client-context]). If you need to provide initial values (similar to `getLoadContext` on the server), you can do so with a new `getContext` method which returns a `Map<RouterContext, unknown>`:
5478

5579
```ts
80+
let loggerContext = unstable_createContext<(...args: unknown[]) => void>();
81+
5682
function getContext() {
5783
return new Map([[loggerContext, (...args) => console.log(...args)]])
5884
}

0 commit comments

Comments
 (0)