Reputation: 3153
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
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
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
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.
I use function component to return Route array.
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
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
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