# raf-effect-atom/react-router

> Connects the loader verbs to React Router's Framework Mode.

A root `middleware` gives each server request its own registry, under a
router context the app creates. Loaders start atoms with its verbs and
return its `dehydrate()` with their data; the route component hydrates it
with `HydrationBoundary`, inside the root's `RegistryProvider`, both from
`raf-effect-atom`. A root `clientMiddleware` puts the browser's registry
under the same context, so one function can be both the route's `loader`
and its `clientLoader`: after the first page, loaders run in the browser,
where `ensure` finds what the registry already holds. Nothing is imported
from `react-router`.

**Example**

```tsx
// app/atoms.ts
import { createContext } from "react-router"
import type { RequestAtoms } from "raf-effect-atom/react-router"

export const atomsContext = createContext<RequestAtoms>()

// app/root.tsx
import { atomsMiddleware } from "raf-effect-atom/react-router"

export const middleware = [atomsMiddleware(atomsContext)]
export const clientMiddleware = [clientAtomsMiddleware(atomsContext)]

// app/routes/issue.tsx
async function load(args: Route.LoaderArgs) {
  const atoms = args.context.get(atomsContext)
  const handle = await atoms.ensure(issue, Number(args.params.id))
  return { atoms: atoms.dehydrate(), issue: handle }
}

export const loader = load
export const clientLoader = load

export default function IssueRoute(props: Route.ComponentProps) {
  return (
    <HydrationBoundary state={props.loaderData.atoms}>
      <Issue issue={props.loaderData.issue} />
    </HydrationBoundary>
  )
}
```

```ts
import { atomsMiddleware, clientAtomsMiddleware } from "raf-effect-atom/react-router";
```

## `RequestAtoms`

<p class="api-meta"><span class="api-kind">interface</span><span>models</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/react-router.ts#L69">Source</a></p>

```ts
interface RequestAtoms extends Loader.Loader {
  /** `Hydration.dehydrate` on the request's registry, for loader data. */
  readonly dehydrate: () => Array<EffectHydration.DehydratedAtom>;
  readonly registry: AtomRegistry.AtomRegistry;
}
```

One request's registry, with the loader verbs over it. In a client loader,
the browser's registry, whose `dehydrate` returns nothing: its atoms are
already where the components read them.

## `AtomsContext`

<p class="api-meta"><span class="api-kind">interface</span><span>models</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/react-router.ts#L83">Source</a></p>

```ts
interface AtomsContext {
  readonly defaultValue?: RequestAtoms | undefined;
}
```

The router context `atomsMiddleware` sets: what `react-router`'s
`createContext<RequestAtoms>()` returns.

## `MiddlewareArgs`

<p class="api-meta"><span class="api-kind">interface</span><span>models</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/react-router.ts#L94">Source</a></p>

```ts
interface MiddlewareArgs {
  readonly context: {
    readonly set: (context: AtomsContext, value: RequestAtoms) => void;
  };
  readonly request: Request;
}
```

The parts of React Router's middleware arguments `atomsMiddleware` uses.

## `AtomsMiddlewareOptions`

<p class="api-meta"><span class="api-kind">interface</span><span>models</span><span>since 0.2.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/react-router.ts#L108">Source</a></p>

```ts
interface AtomsMiddlewareOptions {
  /**
   * Runs on each request's registry before the loaders, with the request,
   * to set what its atoms need, such as its cookie. The middleware waits
   * for one that returns a promise.
   */
  readonly seed?:
    | ((
        registry: AtomRegistry.AtomRegistry,
        request: Request,
      ) => void | Promise<void>)
    | undefined;
}
```

Options for `atomsMiddleware`.

## `atomsMiddleware`

<p class="api-meta"><span class="api-kind">const</span><span>integrations</span><span>since 0.1.0</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/react-router.ts#L162">Source</a></p>

```ts
const atomsMiddleware: (
  context: AtomsContext,
  options?: AtomsMiddlewareOptions,
) => <Result>(
  args: MiddlewareArgs,
  next: () => Promise<Result>,
) => Promise<Result>;
```

Creates a root middleware that gives each server request its own
registry, under the given router context.

**Details**

The registry is disposed once the response's body has been sent, or the
client has gone: loader data streams its pending promises after the
middleware returns, so the atoms behind them must keep running until
then. When `next` doesn't return a `Response`, as Data Mode's
`query` can, the registry is left to be collected with the request.

`seed` runs on the new registry before `next`, so every loader's atoms
start with what it sets.

The verbs take the request's `signal`: when the client goes before the
response, such as a navigation that leaves while its loaders wait, the
`ensure` and `fetch` calls still waiting reject with its reason.

**Gotchas**

It's server middleware: export it as the root route's `middleware`, not
`clientMiddleware`, which takes `clientAtomsMiddleware`.

**Example**

```ts
export const middleware = [
  atomsMiddleware(atomsContext, {
    // In-process RPC calls send the page request's cookie.
    seed: (registry, request) =>
      registry.set(requestCookie, request.headers.get("cookie") ?? undefined)
  })
]
```

## `clientAtomsMiddleware`

<p class="api-meta"><span class="api-kind">const</span><span>integrations</span><span>since 0.2.1</span><a href="https://github.com/KATT/raf-effect-atom/blob/main/packages/raf-effect-atom/src/react-router.ts#L286">Source</a></p>

```ts
const clientAtomsMiddleware: (
  context: AtomsContext,
) => <Result>(
  args: MiddlewareArgs,
  next: () => Promise<Result>,
) => Promise<Result>;
```

Creates a root client middleware that gives client loaders the browser's
registry, under the context `atomsMiddleware` uses on the server.

**When to use**

Export it as the root route's `clientMiddleware`, and each route's loader
function as both its `loader` and its `clientLoader`. The first page's loaders run on
the server and stream their atoms in; after that, navigations run the
loaders in the browser. `ensure` then renders what the browser's registry
holds without a request, and a link's prefetch doesn't fetch a route's
`.data` that the click would fetch again.

**Details**

The registry is the one the mounted `RegistryProvider` provides. Its
`dehydrate` returns nothing, so a loader's data has the same shape in
the browser and on the server, and `HydrationBoundary` has nothing to do.
The verbs take the navigation's `signal`: when another navigation
interrupts it, the `ensure` and `fetch` calls still waiting reject with
its reason, and the atoms keep loading.

**Gotchas**

The atoms run in the browser, so their RPC protocol must reach the server
from there, as it does for anything a component starts.

**Example**

```ts
// app/root.tsx
export const middleware = [atomsMiddleware(atomsContext)]
export const clientMiddleware = [clientAtomsMiddleware(atomsContext)]

// app/routes/issue.tsx: one function for both, since React Router
// removes `loader` from the browser's bundle.
export const loader = load
export const clientLoader = load
```
