最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - react component is not displayed with react router v7 - Stack Overflow

programmeradmin5浏览0评论

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
  • Please edit to include a complete minimal reproducible example showing the descendent routes you are rendering and what buttons you are clicking on to navigate so readers can better help you. – Drew Reese Commented Mar 20 at 14:09
  • I update my post with more information – Vana Commented Mar 20 at 15:10
  • Are you trying to render nested children routes from "/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
  • I would like to render nested children from /admin routes with the same layout – Vana Commented Mar 21 at 11:03
  • The 2 routes, /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
 |  Show 4 more comments

1 Answer 1

Reset to default -1

I have found two solution for that:

  1. 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

  1. 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>
  );
}
发布评论

评论列表(0)

  1. 暂无评论