How to type the StaticRouterContext from react-router-dom in Typescript?

Viewed 1182

I'm doing SSR for my app (for robots only).

The render of the <App/> might result in a not found route.

I need to detect when that render ends in a NOT_FOUND route so I can return a res.sendStatus(404); from my express server.

I'm following this tutorial from the official docs of react-router.

I have a "catch all" route, that will render a 404 Page and will add that information to the staticContext:

AllRoutes.ts

<Route exact path={"*"} render={({ staticContext }) => { 
  if (staticContext) {
    staticContext.statusCode = 404;
  }
  return <Page404/>;
}}/>

The type inferred for the staticContext object inside the render prop of the Route component is:

enter image description here

And it seems that its only property is an optional statusCode?: number property.

enter image description here

On the SERVER side, this is what I do:

ssrApp.tsx

import App from "@src/App";
import { renderToString } from "react-dom/server";
import { StaticRouter } from "react-router-dom";

// ...

const context = {};  // <<< WHAT TO TYPE THIS VARIABLE ?

const appHtml = renderToString(
  <StaticRouter location={req.url} context={context}>
    <App/>
  </StaticRouter>
);

res.status(context.statusCode || 200).send(appHtml);

The type for the context prop of the <StaticRouter> component is StaticRouterContext:

enter image description here

It accepts the empty const context= {}; prop. But it complains when I try to access the statusCode property out of it.

enter image description here


All the types mentioned here seem to come from the package @types/react-router or @types/react-router-dom, not really sure which one.

@types/react-router/index.d.ts

export interface StaticContext {
    statusCode?: number;
}

export interface RouteComponentProps<
    Params extends { [K in keyof Params]?: string } = {},
    C extends StaticContext = StaticContext,
    S = H.LocationState
> {
    history: H.History<S>;
    location: H.Location<S>;
    match: match<Params>;
    staticContext?: C;
}

How can I type my context object? So far, I haven't been able to import the StaticRouterContext or StaticContext from anywhere.

1 Answers

Just found a way to handle this.

I only have installed the following packages:

"@types/react-router-dom": "^5.1.5" (AS DEV DEPENDENCY)
"react-router-dom": "^5.2.0"        (AS REGULAR DEPENDENCY)

I'm not able to import the types I've mentioned in the question because they are defined in @types/react-router, which is a inner dependency of @types/react-router-dom, and they are not re-exported from the latter.

Here are the types from @types/react-router that are re-exported from @types/react-router-dom:

@types/react-router-dom/index.d.ts

enter image description here

What I was able to do:

I've created my own MY_STATIC_CONTEXT type, which needs to implicitly extends the StaticContext type defined in @types/react-router which is used in StaticRouter and RouteProps.

This is the StaticContext type from @types/react-router:

export interface StaticContext {
    statusCode?: number;
}

So I did:

type MY_STATIC_CONTEXT = {
  statusCode?: number        // THIS IS SO YOU EXTEND THE ORIGINAL TYPE,
  ROUTE_NOT_FOUND: boolean   // THIS IS MY CUSTOM PROP
}

Now, this is possible:

ON_CLIENT - AllRoutes.ts

A simple type assention allows me to updated my custom prop ROUTE_NOT_FOUND.

<Route exact path={"*"} render={({ staticContext }) => { 
  if (staticContext) {
    (staticContext as MY_STATIC_CONTEXT).statusCode = 404; 
    (staticContext as MY_STATIC_CONTEXT).ROUTE_NOT_FOUND = true;
  }
  return <Error404/>;
}}/>

ON_SERVER - ssrApp.tsx

const context: MY_STATIC_CONTEXT = {
  ROUTE_NOT_FOUND: false
};

const appHtml = renderToString(
  <StaticRouter location={req.url} context={context}>
      <App/>
  </StaticRouter>
));

if (context.ROUTE_NOT_FOUND) {
  // DO SOMETHING
}

res.status(context.statusCode || 200).send(appHtml);
Related