I'm trying to implement nested routing in my react app The routes in my root app.jsx file are defined like this.
<>
<Router>
<DrawerContextProvider>
<Appbar />
<Switch>
<Route exact path='/admin'>
<Admin/>
</Route>
<Route path='/technician' component={Technician} />
<Route path='/accountmanager' component={AccountManager} />
</Switch>
</DrawerContextProvider>
</Router>
</>
This is the code in my admin component
function Admin() {
const classes = useStyles();
const { open } = useContext(DrawerContext)
const { url, path } = useRouteMatch();
return (
<>
<SideMenu iconsandnames={iconsandnames} />
<div>
<main
className={clsx(classes.content, {
[classes.contentShift]: open,
})}
>
<div className={classes.drawerHeader} />
<Switch>
<Route exact path={path} >
<Dashboard />
</Route>
<Route path={`${path}/:id`} >
<Outlet />
</Route>
</Switch>
</main>
</div>
</>
)
}
function Outlet() {
const { id } = useParams();
console.log(id);
return (
<div>
<h3>{id}</h3>
</div>
);
}
This is the code in my Side menu component
export default function MiniDrawer({ iconsandnames }, props) {
const classes = useStyles();
const theme = useTheme();
const { url, path } = useRouteMatch();
const { open, handleDrawerClose } = useContext(DrawerContext)
return (
<div >
<Drawer
variant="permanent"
className={clsx(classes.drawer, {
[classes.drawerOpen]: open,
[classes.drawerClose]: !open,
})}
classes={{
paper: clsx({
[classes.drawerOpen]: open,
[classes.drawerClose]: !open,
}),
}}
>
<div className={classes.toolbar}>
<IconButton onClick={handleDrawerClose}>
{theme.direction === 'rtl' ? <ChevronRightIcon /> : <ChevronLeftIcon />}
</IconButton>
</div>
<Divider />
<List>
{iconsandnames.map((data) => (
data.text == "Home" ? (
<Link to={url}>
<ListItem button key={data.text} >
<ListItemIcon><Icon>{data.iconname}</Icon></ListItemIcon>
<ListItemText primary={data.text} />
</ListItem>
</Link>):
( <Link to={`${url}/${data.route}`}>
<ListItem button key={data.text} >
<ListItemIcon><Icon>{data.iconname}</Icon></ListItemIcon>
<ListItemText primary={data.text} />
</ListItem>
</Link>)
))}
</List>
</Drawer>
</div>
);
}
this is the iconsandnames array I'm passing into the sidemenu
const iconsandnames = [
{
iconname: 'home',
text: 'Home',
route: 'home'
},
{
iconname: 'rounded_corner',
text: 'Projects',
route: 'projects'
},
{
iconname: 'account_box',
text: 'Account managers',
route: 'accountmanagers'
},
{
iconname: 'build',
text: 'Contractors',
route: 'contractors'
},
{
iconname: 'perm_identity',
text: 'Clients',
route: 'clients'
},
{
iconname: 'work_outline',
text: 'Work Orders',
route: 'workorders'
},
{
iconname: 'preview',
text: 'Additional',
route: 'additional'
},
]
http://localhost:3000/admin shows me
http://localhost:3000/admin/projects shows me
ideally the sidemenu should not disappear when I switch the route but instead it is disappearing over here. Any ideas on what might be the problem and how can I solve it?
You are specifying the exact
prop on the "/admin" path, so any nested routes can no longer be matched and rendered.
<Route exact path='/admin'>
<Admin/>
</Route>
Admin
<Switch>
<Route exact path={path} > // <-- can match
<Dashboard />
</Route>
<Route path={`${path}/:id`} > // <-- can't match
<Outlet />
</Route>
</Switch>
When rendering Route
components into a Switch
, path order and specificity matter. You will want to order your paths from more specific to less specific, in order to give the more specific paths a chance to be matched and rendered first before less specific paths.
Routes - remove the exact
prop from the "/admin" route so any nested route can be matched.
<>
<Router>
<DrawerContextProvider>
<Appbar />
<Switch>
<Route path='/admin'>
<Admin/>
</Route>
<Route path='/technician' component={Technician} />
<Route path='/accountmanager' component={AccountManager} />
</Switch>
</DrawerContextProvider>
</Router>
</>
Admin - reorder the routes by specificity.
<Switch>
<Route path={`${path}/:id`} >
<Outlet />
</Route>
<Route path={path} >
<Dashboard />
</Route>
</Switch>