Skip to content

RouteParams & QueryParams #9

@bbenezech

Description

@bbenezech

I love this lib, simple and focused.

I think you may want to spend another hour of work to:

  • add QueryParams type, modelled after RouteParams
  • add documentation for QueryParams & RouteParams
  • add documentation for how to type ReactRouter's Route component's params

User-land typesafe usage example of ReactRouter Route component:

import React from "react";
import { Route as RouteComponent, RouteProps, RouteComponentProps } from "react-router-dom";
import { RouteParams, Route } from "typesafe-react-router";

type QueryParams<T> = T extends Route<any, infer Y> ? Partial<Record<Y[number], string>> : never;

interface MatchProps<R extends Route<any, any>>
  extends Omit<RouteProps, "path" | "render" | "component" | "children"> {
  route: R;
  component?: React.ComponentType<RouteComponentProps<RouteParams<R>>>;
  render?: (
    params: RouteParams<R> & QueryParams<R>,
    allProps: RouteComponentProps<RouteParams<R>>
  ) => React.ReactNode;
  children?: (props: RouteComponentProps<RouteParams<R>>) => React.ReactNode;
}

// Simple typesafe ReactRoute `Route` component wrapper. Not nestable in ReactRouter `Switch` component.

// `render` prop.
// Renders only when matching.
// Similar to `ReactRouter Route#render`, but first argument is params/search_params hash:
//   Explicitely access match params/search_params hash.
//     <Match route={Routes.project.template()} render={({ projectId }) => <Project projectId={projectId} />} />
//   Project -since it is a function- gets the first render arg. (i.e. params/search_params) mixed in the props.
//   So the previous example can be written:
//     <Match route={Routes.project.template()} render={Project} />
//   Explicitely access ReactRouter props (second arg.).
//     <Match route={Routes.project.template()} render={(_, { history, location, match, staticContext }) => <Project projectId={match.params.projectId} /> } />

// `component` prop.
// Renders only when matching.
// Identical to `ReactRouter Route#component`:
// Project gets ReactRouter props mixed in his props. `projectId` is accessible with `props.match.params.projectId`.
//    <Match route={Routes.project.template()} component={Project} />

// `children` prop
// ALWAYS renders
// Identical to `ReactRouter Route#children`. Only the "function as a children" form is typesafe.
//   <Match route={Routes.project.template()}>
//     {({ history, location, match, staticContext }) =>
//       match ? <Project projectId={match.params.projectId} /> : null}
//   </Match>

const Match = <R extends Route<any, any>>({
  route,
  render,
  component,
  ...routeProps
}: MatchProps<R>) => (
  <RouteComponent
    {...routeProps}
    path={route.template()}
    render={props =>
      render
        ? render({ ...props.match.params, ...route.parse(props.location.search) }, props)
        : component
        ? React.createElement(component, props)
        : undefined
    }
  />
);

export default Match;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions