I do
function PrivateRoutes() {
const user = useAuth();
return user.token ? <Outlet /> : <Navigate to="/login" replace />;
}
const routes = (isInstalled: boolean) => [
element: <PrivateRoutes />,
children: [
{
path: "admin",
element: <Dashboard />,
children: [
{ index: true, element: <Index /> },
{ path: "foo", element: <Foo/> },
{ path: "bar", element: <Bar/> },
],
},
],
},
],
Only the /admin/foo and /admin/bar are reachable and react component are displayed
/admin route render only the layout. component is not rendered.
I have also a Sidebar component, which is imported into layout:
import { useEffect, useState } from "react";
// react-router-dom components
import { useLocation, NavLink } from "react-router-dom";
// prop-types is a library for typechecking of props.
import PropTypes from "prop-types";
// @mui material components
import List from "@mui/material/List";
import Divider from "@mui/material/Divider";
import Link from "@mui/material/Link";
import Icon from "@mui/material/Icon";
import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore";
import LogoutIcon from "@mui/icons-material/Logout";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import ListItemIcon from "@mui/material/ListItemIcon";
import Collapse from "@mui/material/Collapse";
// Material Dashboard 2 React components
import Box from "components/Box";
import Typography from "components/Typography";
import Avatar from "components/Avatar";
// Material Dashboard 2 React example components
import SidenavCollapse from "components/Sidenav/SidenavCollapse";
// Custom styles for the Sidenav
import SidenavRoot from "components/Sidenav/SidenavRoot";
import sidenavLogoLabel from "components/Sidenav/styles/sidenav";
import burceMars from "assets/images/bruce-mars.jpg";
// Material Dashboard 2 React context
import {
useMaterialUIController,
setMiniSidenav,
setTransparentSidenav,
setWhiteSidenav,
} from "context";
import { useAuth } from "context/Auth";
function Sidenav({ brand = "", brandName, routes, ...rest }) {
const [openLoginMenu, setOpenLoginMenu] = useState();
const [controller, dispatch] = useMaterialUIController();
const { miniSidenav, transparentSidenav, whiteSidenav, darkMode } =
controller;
const auth = useAuth();
const location = useLocation();
const collapseName = location.pathname.replace("/", "");
let textColor = "white";
if (transparentSidenav || (whiteSidenav && !darkMode)) {
textColor = "dark";
} else if (whiteSidenav && darkMode) {
textColor = "inherit";
}
const closeSidenav = () => setMiniSidenav(dispatch, true);
useEffect(() => {
// A function that sets the mini state of the sidenav.
function handleMiniSidenav() {
setMiniSidenav(dispatch, window.innerWidth < 1200);
setTransparentSidenav(
dispatch,
window.innerWidth < 1200 ? false : transparentSidenav,
);
setWhiteSidenav(
dispatch,
window.innerWidth < 1200 ? false : whiteSidenav,
);
}
/**
The event listener that's calling the handleMiniSidenav function when resizing the window.
*/
window.addEventListener("resize", handleMiniSidenav);
// Call the handleMiniSidenav function to set the state with the initial value.
handleMiniSidenav();
// Remove event listener on cleanup
return () => {
window.removeEventListener("resize", handleMiniSidenav);
};
}, [dispatch, location]);
// Render all the routes from the routes.js (All the visible items on the Sidenav)
const renderRoutes = routes.map(
({ type, name, icon, title, noCollapse, key, href, route }) => {
let returnValue;
if (type === "collapse") {
returnValue = href ? (
<Link
href={href}
key={key}
target="_blank"
rel="noreferrer"
sx={{ textDecoration: "none" }}
>
<SidenavCollapse
name={name}
icon={icon}
active={key === collapseName}
noCollapse={noCollapse}
/>
</Link>
) : (
<NavLink key={key} to={route}>
<SidenavCollapse
name={name}
icon={icon}
active={key === collapseName}
/>
</NavLink>
);
} else if (type === "title") {
returnValue = (
<Typography
key={key}
color={textColor}
display="block"
variant="caption"
fontWeight="bold"
textTransform="uppercase"
pl={3}
mt={2}
mb={1}
ml={1}
>
{title}
</Typography>
);
} else if (type === "divider") {
returnValue = (
<Divider
key={key}
light={
(!darkMode && !whiteSidenav && !transparentSidenav) ||
(darkMode && !transparentSidenav && whiteSidenav)
}
/>
);
}
return returnValue;
},
);
return (
<SidenavRoot
{...rest}
variant="permanent"
ownerState={{ transparentSidenav, whiteSidenav, miniSidenav, darkMode }}
>
<Box pt={3} pb={1} px={4} textAlign="center">
<Box
display={{ xs: "block", xl: "none" }}
position="absolute"
top={0}
right={0}
p={1.625}
onClick={closeSidenav}
sx={{ cursor: "pointer" }}
>
<Typography variant="h6" color="secondary">
<Icon sx={{ fontWeight: "bold" }}>close</Icon>
</Typography>
</Box>
<Box component={NavLink} to="/" display="flex" alignItems="center">
{brand && (
<Box component="img" src={brand} alt="Brand" width="2rem" />
)}
<Box
width={!brandName && "100%"}
sx={(theme) => sidenavLogoLabel(theme, { miniSidenav })}
>
<Typography
component="h6"
variant="button"
fontWeight="medium"
color={textColor}
>
{brandName}
</Typography>
</Box>
</Box>
</Box>
<Divider
light={
(!darkMode && !whiteSidenav && !transparentSidenav) ||
(darkMode && !transparentSidenav && whiteSidenav)
}
/>
<List>
<ListItemButton
onClick={() => {
setOpenLoginMenu(!openLoginMenu);
}}
>
<Avatar src={burceMars} alt="profile" size="md" shadow="md" />
<ListItemText sx={{ color: "#f0f2f5" }} primary={auth.username} />
{openLoginMenu ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={openLoginMenu} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItemButton onClick={() => auth.Logout()} sx={{ pl: 4 }}>
<ListItemIcon>
<LogoutIcon />
</ListItemIcon>
<ListItemText sx={{ color: "#f0f2f5" }} primary="Logout" />
</ListItemButton>
</List>
</Collapse>
</List>
<Divider
light={
(!darkMode && !whiteSidenav && !transparentSidenav) ||
(darkMode && !transparentSidenav && whiteSidenav)
}
/>
<List>{renderRoutes}</List>
</SidenavRoot>
);
}
// Typechecking props for the Sidenav
Sidenav.propTypes = {
color: PropTypes.oneOf([
"primary",
"secondary",
"info",
"success",
"warning",
"error",
"dark",
]),
brand: PropTypes.string,
brandName: PropTypes.string.isRequired,
routes: PropTypes.arrayOf(PropTypes.object).isRequired,
};
export default Sidenav;
Layout:
<Sidenav
color={sidenavColor}
brand={
(transparentSidenav && !darkMode) || whiteSidenav
? brandDark
: brandWhite
}
brandName="Lucle 2"
routes={adminroutes}
onMouseEnter={handleOnMouseEnter}
onMouseLeave={handleOnMouseLeave}
/>
<Routes>{getRoutes(adminroutes)}</Routes>
and adminRoutes:
import Icon from "@mui/material/Icon";
import Bar from "views/Bar";
import Foo from "views/Foo";
const adminroutes = [
{
type: "collapse",
name: "Home",
key: "admin",
icon: <Icon fontSize="small">dashboard</Icon>,
},
{
type: "collapse",
name: "Foo",
key: "foo",
icon: <Icon fontSize="small">dashboard</Icon>,
route: "foo",
component: <Foo/>,
},
{
type: "collapse",
name: "bar",
key: "bar",
icon: <Icon fontSize="small">dashboard</Icon>,
route: "launcher",
component: <Bar/>,
},
];
export default adminroutes;
Into console, I get
You rendered descendant <Routes> (or called `useRoutes()`) at "/admin" (under <Route path="admin">) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render.
Please change the parent <Route path="admin"> to <Route path="admin/*">. react-router-dom.js:1197:49
If I do admin/*, On each click to a button, the segment is add. I can see /foo/bar/foo/bar ...
I don't understand what is wrong
I do
function PrivateRoutes() {
const user = useAuth();
return user.token ? <Outlet /> : <Navigate to="/login" replace />;
}
const routes = (isInstalled: boolean) => [
element: <PrivateRoutes />,
children: [
{
path: "admin",
element: <Dashboard />,
children: [
{ index: true, element: <Index /> },
{ path: "foo", element: <Foo/> },
{ path: "bar", element: <Bar/> },
],
},
],
},
],
Only the /admin/foo and /admin/bar are reachable and react component are displayed
/admin route render only the layout. component is not rendered.
I have also a Sidebar component, which is imported into layout:
import { useEffect, useState } from "react";
// react-router-dom components
import { useLocation, NavLink } from "react-router-dom";
// prop-types is a library for typechecking of props.
import PropTypes from "prop-types";
// @mui material components
import List from "@mui/material/List";
import Divider from "@mui/material/Divider";
import Link from "@mui/material/Link";
import Icon from "@mui/material/Icon";
import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore";
import LogoutIcon from "@mui/icons-material/Logout";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import ListItemIcon from "@mui/material/ListItemIcon";
import Collapse from "@mui/material/Collapse";
// Material Dashboard 2 React components
import Box from "components/Box";
import Typography from "components/Typography";
import Avatar from "components/Avatar";
// Material Dashboard 2 React example components
import SidenavCollapse from "components/Sidenav/SidenavCollapse";
// Custom styles for the Sidenav
import SidenavRoot from "components/Sidenav/SidenavRoot";
import sidenavLogoLabel from "components/Sidenav/styles/sidenav";
import burceMars from "assets/images/bruce-mars.jpg";
// Material Dashboard 2 React context
import {
useMaterialUIController,
setMiniSidenav,
setTransparentSidenav,
setWhiteSidenav,
} from "context";
import { useAuth } from "context/Auth";
function Sidenav({ brand = "", brandName, routes, ...rest }) {
const [openLoginMenu, setOpenLoginMenu] = useState();
const [controller, dispatch] = useMaterialUIController();
const { miniSidenav, transparentSidenav, whiteSidenav, darkMode } =
controller;
const auth = useAuth();
const location = useLocation();
const collapseName = location.pathname.replace("/", "");
let textColor = "white";
if (transparentSidenav || (whiteSidenav && !darkMode)) {
textColor = "dark";
} else if (whiteSidenav && darkMode) {
textColor = "inherit";
}
const closeSidenav = () => setMiniSidenav(dispatch, true);
useEffect(() => {
// A function that sets the mini state of the sidenav.
function handleMiniSidenav() {
setMiniSidenav(dispatch, window.innerWidth < 1200);
setTransparentSidenav(
dispatch,
window.innerWidth < 1200 ? false : transparentSidenav,
);
setWhiteSidenav(
dispatch,
window.innerWidth < 1200 ? false : whiteSidenav,
);
}
/**
The event listener that's calling the handleMiniSidenav function when resizing the window.
*/
window.addEventListener("resize", handleMiniSidenav);
// Call the handleMiniSidenav function to set the state with the initial value.
handleMiniSidenav();
// Remove event listener on cleanup
return () => {
window.removeEventListener("resize", handleMiniSidenav);
};
}, [dispatch, location]);
// Render all the routes from the routes.js (All the visible items on the Sidenav)
const renderRoutes = routes.map(
({ type, name, icon, title, noCollapse, key, href, route }) => {
let returnValue;
if (type === "collapse") {
returnValue = href ? (
<Link
href={href}
key={key}
target="_blank"
rel="noreferrer"
sx={{ textDecoration: "none" }}
>
<SidenavCollapse
name={name}
icon={icon}
active={key === collapseName}
noCollapse={noCollapse}
/>
</Link>
) : (
<NavLink key={key} to={route}>
<SidenavCollapse
name={name}
icon={icon}
active={key === collapseName}
/>
</NavLink>
);
} else if (type === "title") {
returnValue = (
<Typography
key={key}
color={textColor}
display="block"
variant="caption"
fontWeight="bold"
textTransform="uppercase"
pl={3}
mt={2}
mb={1}
ml={1}
>
{title}
</Typography>
);
} else if (type === "divider") {
returnValue = (
<Divider
key={key}
light={
(!darkMode && !whiteSidenav && !transparentSidenav) ||
(darkMode && !transparentSidenav && whiteSidenav)
}
/>
);
}
return returnValue;
},
);
return (
<SidenavRoot
{...rest}
variant="permanent"
ownerState={{ transparentSidenav, whiteSidenav, miniSidenav, darkMode }}
>
<Box pt={3} pb={1} px={4} textAlign="center">
<Box
display={{ xs: "block", xl: "none" }}
position="absolute"
top={0}
right={0}
p={1.625}
onClick={closeSidenav}
sx={{ cursor: "pointer" }}
>
<Typography variant="h6" color="secondary">
<Icon sx={{ fontWeight: "bold" }}>close</Icon>
</Typography>
</Box>
<Box component={NavLink} to="/" display="flex" alignItems="center">
{brand && (
<Box component="img" src={brand} alt="Brand" width="2rem" />
)}
<Box
width={!brandName && "100%"}
sx={(theme) => sidenavLogoLabel(theme, { miniSidenav })}
>
<Typography
component="h6"
variant="button"
fontWeight="medium"
color={textColor}
>
{brandName}
</Typography>
</Box>
</Box>
</Box>
<Divider
light={
(!darkMode && !whiteSidenav && !transparentSidenav) ||
(darkMode && !transparentSidenav && whiteSidenav)
}
/>
<List>
<ListItemButton
onClick={() => {
setOpenLoginMenu(!openLoginMenu);
}}
>
<Avatar src={burceMars} alt="profile" size="md" shadow="md" />
<ListItemText sx={{ color: "#f0f2f5" }} primary={auth.username} />
{openLoginMenu ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={openLoginMenu} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItemButton onClick={() => auth.Logout()} sx={{ pl: 4 }}>
<ListItemIcon>
<LogoutIcon />
</ListItemIcon>
<ListItemText sx={{ color: "#f0f2f5" }} primary="Logout" />
</ListItemButton>
</List>
</Collapse>
</List>
<Divider
light={
(!darkMode && !whiteSidenav && !transparentSidenav) ||
(darkMode && !transparentSidenav && whiteSidenav)
}
/>
<List>{renderRoutes}</List>
</SidenavRoot>
);
}
// Typechecking props for the Sidenav
Sidenav.propTypes = {
color: PropTypes.oneOf([
"primary",
"secondary",
"info",
"success",
"warning",
"error",
"dark",
]),
brand: PropTypes.string,
brandName: PropTypes.string.isRequired,
routes: PropTypes.arrayOf(PropTypes.object).isRequired,
};
export default Sidenav;
Layout:
<Sidenav
color={sidenavColor}
brand={
(transparentSidenav && !darkMode) || whiteSidenav
? brandDark
: brandWhite
}
brandName="Lucle 2"
routes={adminroutes}
onMouseEnter={handleOnMouseEnter}
onMouseLeave={handleOnMouseLeave}
/>
<Routes>{getRoutes(adminroutes)}</Routes>
and adminRoutes:
import Icon from "@mui/material/Icon";
import Bar from "views/Bar";
import Foo from "views/Foo";
const adminroutes = [
{
type: "collapse",
name: "Home",
key: "admin",
icon: <Icon fontSize="small">dashboard</Icon>,
},
{
type: "collapse",
name: "Foo",
key: "foo",
icon: <Icon fontSize="small">dashboard</Icon>,
route: "foo",
component: <Foo/>,
},
{
type: "collapse",
name: "bar",
key: "bar",
icon: <Icon fontSize="small">dashboard</Icon>,
route: "launcher",
component: <Bar/>,
},
];
export default adminroutes;
Into console, I get
You rendered descendant <Routes> (or called `useRoutes()`) at "/admin" (under <Route path="admin">) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render.
Please change the parent <Route path="admin"> to <Route path="admin/*">. react-router-dom.js:1197:49
If I do admin/*, On each click to a button, the segment is add. I can see /foo/bar/foo/bar ...
I don't understand what is wrong
Share Improve this question edited Mar 20 at 15:09 Vana asked Mar 20 at 13:32 VanaVana 9404 gold badges15 silver badges24 bronze badges 9 | Show 4 more comments1 Answer
Reset to default -1I have found two solution for that:
- Navigation Type
You are using admin/* but with relative navigation means
your route value is without any "/" you are assign values direclty "foo" , "bar"
so solution is you can use absolute navigation with admin/* like below
Use /admin/foo & /admin/bar (absolute path) for navigation.
<button onClick={() => navigate("/admin/foo")}>Go to Foo
- Use useRoutes of router dom.
I have modified code with the use of useRoutes & It works as you expected. Also it will not create an issue with type of navigation.
import { BrowserRouter, Navigate, Outlet, useRoutes } from "react-router-dom";
import Dashboard from "./views/Dashboard";
import Foo from "./views/Foo";
import Bar from "./views/Bar";
import Index from "./views/Index";
import { useAuth } from "./hooks/useAuth";
function PrivateRoutes() {
const user = useAuth();
return user.token ? <Outlet /> : <Navigate to="/login" replace />;
}
const RoutesComponent = () =>
useRoutes([
{
element: <PrivateRoutes />,
children: [
{
path: "admin",
element: <Dashboard />,
children: [
{ index: true, element: <Index /> },
{ path: "foo", element: <Foo /> },
{ path: "bar", element: <Bar /> },
],
},
],
},
]);
export default function AppRoutes() {
return (
<BrowserRouter>
<RoutesComponent />
</BrowserRouter>
);
}
"/admin"
or are you trying to render descendent routes from"/admin/*"
? Can you edit to clarify what you are trying to accomplish and share a complete minimal reproducible example with enough code for readers to run and reproduce the problem? – Drew Reese Commented Mar 20 at 15:50/admin
routes with the same layout – Vana Commented Mar 21 at 11:03/admin/foo
and/admin/bar
works but not/admin
, I can see the layout but not the nested component – Vana Commented Mar 21 at 11:45