sheamus
sheamus

Reputation: 3153

groups of routes with react-router-4

My react app has 3 entry points with overlapping routes, and it is getting hard to maintain. 2 of the apps are basically just stuck in a couple places on the legacy site, until the main app has enough functionality to fully replace the main site.

I am using React router 4, and have a routes.tsx file with all my routes. But I would like to group the routes by function, and then have the routes component for each app just take what it needs.

Currently my routes look like this:

const MainAppRoutes: React.SFC = (): JSX.Element =>
{
    return (
        <Switch>
            <Route exact path='/' component={HomePage} />
            <Route path='/customers' component={CustomersDisplayPage} />
            <Route path='/customers/:id' component={CustomersDisplayPage} />
            <Route path='/cars' component={CarDisplayPage} />
            <Route path='/cars/:id' component={CarDisplayPage} />
        </Switch>
    );
};

But I would like it to look like this:

const MainAppRoutes: React.SFC = (): JSX.Element =>
{
    return (
        <Switch>
            <Route exact path='/' component={HomePage} />
            <CustomerAppRoutes />
            <CarAppRoutes />
        </Switch>
    );

const CustomerAppRoutes: React.SFC = (): JSX.Element =>
{
    return (
        <Switch>
            <Route path='/customers' component={CustomersDisplayPage} />
            <Route path='/customers/:id' component={CustomersDisplayPage} />
        </Switch>
    );
};

const CarAppRoutes: React.SFC = (): JSX.Element =>
{
    return (
        <Switch>
            <Route path='/cars' component={CarDisplayPage} />
            <Route path='/cars/:id' component={CarDisplayPage} />
        </Switch>
    );
};

But this causes Caroutes not to route properly. I have tried using Div's instead, and that does not work either.

Upvotes: 11

Views: 12569

Answers (5)

Alasdair P
Alasdair P

Reputation: 67

A bit late to the party but to extend what has already been said: It is correct that < Switch /> will ONLY accept a < Route /> and frustratingly not a regular functional / class component.

We can however, satisfy that requirement by extending Route to be able to create an abstracted set of routes:

MainRoutes.js

import ...
import CarAppRoutes from 'wherever';
import CustomerRoutes from 'wherever';

...
<Switch>
    <Route exact path='/' component={HomePage} />
    <CustomerAppRoutes />
</Switch>

CustomerAppRoutes.js

import ...

class CustomerAppRoutes extends Route { 
  constructor(props) {
    super(props);
  }

  // NB for Switch to accept our route as valid
  static defaultProps = { 
    path: "/customers",
  };


  render() {
    return (
      <React.Fragment>
         <Route path='/customers' component={CustomersDisplayPage} />
         <Route path='/customers/:id' component={CustomersDisplayPage} />
      </React.Fragment>
    )
  }
}

export default CustomerAppRoutes

Upvotes: 0

Madhu Kumar
Madhu Kumar

Reputation: 405

or just map it out as children of switch

 <Switch>
    <Route exact path='/' component={HomePage} />
    {
       ['/customers', '/customers/:id'].map((p, i) => <Route key={i} path={p} component={CustomersDisplayPage} />)
    }
</Switch>

Upvotes: 0

NtsDK
NtsDK

Reputation: 971

I found this question when I try to solve similar problem with react-router-5. Fragments didn't work for me and actually when I look inside Switch component implementation I understand why. Fragment is still a react component but Switch expect only Routes as children.

In my case I can use a workaround.

  1. I use function component to return Route array.

  2. I call function component as function and not as react component.

There are no problems to have props in CustomerAppRoutes and CarAppRoutes if it is necessary.

const MainAppRoutes = () => (
    <Switch>
        <Route exact path='/' component={HomePage} />
        {CustomerAppRoutes()}
        {CarAppRoutes()}
    </Switch>
);

const CustomerAppRoutes = () => ([
    <Route path='/customers' component={CustomersDisplayPage} />,
    <Route path='/customers/:id' component={CustomersDisplayPage} />
]);

const CarAppRoutes = () => ([
    <Route path='/cars' component={CarDisplayPage} />,
    <Route path='/cars/:id' component={CarDisplayPage} />
]);

Upvotes: 6

Gibby
Gibby

Reputation: 221

You could barrel it in separate files and then map them in the main file

CustomerRoutes.js

import ...
export default [
    { path: '/customers', component: CustomersDisplayPage },
    { path: '/customers/:id', component: CustomersDisplayPage }
]

CarAppRoutes.js

import ...
export default [
    { path: '/cars', component: CarDisplayPage },
    { path: '/cars/:id', component: CarDisplayPage }
]

MainRoutes.js

import ...
import CarAppRoutes from 'wherever';
import CustomerRoutes from 'wherever';

...
<Switch>
    <Route exact path='/' component={HomePage} />
    { CustomerRoutes.map(props => <Route {...props} />) }
    { CarAppRoutes.map(props => <Route {...props} />) }
</Switch>

Upvotes: 9

sheamus
sheamus

Reputation: 3153

The solution I am currently using is to create arrays of routes:

//routes.tsx
import * as React from 'react';

export interface IRoute { path: string, component: React.ComponentClass, exact?: boolean }

const CarRoutes: IRoute[] = [
   { path: '/cars', component: {CarDisplayPage} },
   { path: '/cars/:id', component: {CarDisplayPage} },
];

const CustomerRoutes: IRoute[] = [
    { path: '/customers', component: {CustomerDisplayPage} },
    { path: '/customers/:id', component: {CustomerDisplayPage} },
];

const AppRouting: React.SFC = (): JSX.Element =>
{
    const routes = []
        .concat(CarRoutes)
        .concat(CustomerRoutes);
    return (
        <Switch>
            <Route exact path='/' component={HomePage} />
            {routes.map(r => <Route path={r.path} exact={r.exact===true} path={r.path} key={r.path} />)}
        </Switch>
    );
};

Upvotes: 0

Related Questions