React Router Equivalent of render props in Router v6

Viewed 67

I found this interesting piece of code (here) that generates breadcrumbs together with dynamically rendered routes:

routes.ts

  { path: "/home", name: "Home", Component: Home },
  { path: "/dashboard", name: "Dashboard", Component: Dashboard },
  { path: "/all-users", name: "All users", Component: AllUsers  },
  { path: "/user/:id", name: "User", Component: User },
  { path: "/all-contracts", name: "All contracts", Component: AllContracts },
  { path: "/login", name: "Login", Component: Login }
];

App.tsx

        <Router>
           <Switch>

                 { routes.map(({ path, Component }, key) => (
                        <Route
                          path={path}
                          key={key}
                          
                          render={ (props:any) => {
                              const crumbs = routes
                                .filter(({ path }) => props.match.path.includes(path))
                                .map(({ path, ...rest }) => ({
                                  path: Object.keys(props.match.params).length
                                    ? Object.keys(props.match.params)
                                        .reduce((path, param) => path.replace(`:${param}`, props.match.params[param]), path )
                                    : path, ...rest }))

                              return (
                                <>
                                  <BreadCrumbs crumbs={crumbs} />
                                  <Component {...props} />
                                </>
                              );
                           }}
                         />
                     ))}


            </Switch>
         </Router>

This is react router V5 technology, and I cannot figure out how to translate this in a context with router V6 (+ Typescript!), where Route does not accept a render(props) method... If anyone can see any clearer in this than me, it would be wonderful : ) Thanks in advance !

1 Answers

Convert the routes into a routes config so the routed component is passed on the element prop. Create a layout route that reads the current pathname and string splits it to get each path segment and rebuilds an array of crumbs. BreadCrumbLayout renders an Outlet for nested routes to render their matched content out to.

Example:

const BreadCrumbs = ({ crumbs = [] }) =>
  crumbs.map(({ crumb, label }, i) => (
    <Fragment key={crumb}>
      {i !== 0 && " > "}
      <Link to={crumb}>{label}</Link>
    </Fragment>
  ));

const BreadCrumbLayout = () => {
  const { pathname } = useLocation();

  const crumbs = pathname
    .slice(1)
    .split("/")
    .reduce((crumbs, crumb) => {
      return crumbs.concat({
        crumb: crumbs
          .map(({ crumb }) => crumb)
          .concat(crumb)
          .join("/"),
        label: crumb
      });
    }, []);

  return (
    <>
      <BreadCrumbs crumbs={crumbs} />
      <Outlet />
    </>
  );
};

config

const routes = [
  {
    element: <BreadCrumbLayout />,
    children: [
      { path: "/home", name: "Home", element: <Home /> },
      { path: "/dashboard", name: "Dashboard", element: <Dashboard /> },
      { path: "/all-users", name: "All users", element: <AllUsers /> },
      { path: "/user/:id", name: "User", element: <User /> },
      {
        path: "/all-contracts",
        name: "All contracts",
        element: <AllContracts />
      },
      { path: "/login", name: "Login", element: <Login /> }
    ]
  },
  {
    path: "/",
    element: <Navigate to="/home" replace />
  }
];

Usage:

const appRoutes = useRoutes(routes);

...

return (
  <div className="App">
    {appRoutes}
  </div>
);

enter image description here

Edit magical-shockley-qfogov

Related