繁体   English   中英

在子路由内部导航无法正常工作 - React Router v6

[英]Navigate inside a child route not working properly - React Router v6

我正在使用 React Router v6 来构建嵌套路由。 我面临两个问题:

  1. 如果我单击具有子级的链接,则 url 应自动转到子级,但仅呈现组件。 URL 仍然显示“/*”。
  2. 在我的孩子里面,我有一个链接可以让我了解整个路径。 例如,它应该是 '/routeC/subC3/newRoute'

请帮忙。

这是我的代码。

应用程序.js

import "./styles.css";
import {
  Navigate,
  Route,
  Routes,
  useMatch,
  useLocation,
  BrowserRouter,
  Link,
  Outlet
} from "react-router-dom";
import ComponentC from "./ComponentC";
import { Fragment } from "react";

const ComponentA = () => <p>Component A</p>;
const ComponentB = () => <p>Component B</p>;

const ComponentC1 = () => <p>I am in Component C1</p>;
const ComponentC2 = () => <p>I am in Component C2</p>;
const SubComponentC3 = () => <p>SubComponent C3</p>;

export const ComponentC3 = () => {
  const location = useLocation();
  const match = useMatch(location.pathname);
  return (
    <>
      <p>Component C3</p>
      <Link to={`${match.path}/newRoute`}>Take me to a new route</Link>
      <Routes>
        <Route
          exact
          path={`${match.path}/newRoute`}
          element={<SubComponentC3 />}
        />
      </Routes>
    </>
  );
};

export const componentCChildren = [
  {
    label: "Component C - 1",
    code: "subC1",
    component: ComponentC1
  },
  {
    label: "Component C - 2",
    code: "subC2",
    component: ComponentC2
  },
  {
    label: "Component C - 3",
    code: "subC3",
    component: ComponentC3
  }
];

export const routeValues = [
  {
    label: "Component A",
    path: "/routeA",
    component: ComponentA,
    children: []
  },
  {
    label: "Component B",
    path: "/routeB",
    component: ComponentB,
    children: []
  },
  {
    label: "Component C",
    path: "/routeC/*",
    component: ComponentC,
    children: componentCChildren
  }
];

export default function App() {
  return (
    <div className="App">
      <BrowserRouter>
        {routeValues.map((item) => (
          <Link key={item.path} to={item.path} style={{ paddingRight: "10px" }}>
            {item.label}
          </Link>
        ))}
        <Routes>
          {routeValues.map((route) => {
            if (route.children.length > 0) {
              return (
                <Route
                  key={route.path}
                  path={route.path}
                  element={<route.component />}
                >
                  {route.children.map((r, i, arr) => (
                    <Fragment key={r.code}>
                      <Route
                        path={`${route.path}/${r.code}`}
                        element={<r.component />}
                      />
                      <Route
                        path={route.path}
                        element={<Navigate to={`${route.path}/${arr[0].code}`} />}
                      />
                    </Fragment>
                  ))}
                </Route>
              );
            }

            return (
              <Route
                key={route.path}
                path={route.path}
                element={<route.component />}
              />
            );
          })}
          <Route path="*" element={<Navigate to="routeA" />} />
        </Routes>
        <Outlet />
      </BrowserRouter>
    </div>
  );
}

组件C.js

import { useState } from "react";
import Tab from "@mui/material/Tab";
import Box from "@mui/material/Box";
import TabContext from "@mui/lab/TabContext";
import TabList from "@mui/lab/TabList";
import TabPanel from "@mui/lab/TabPanel";
import { useNavigate, useMatch, useLocation } from "react-router-dom";

import { componentCChildren } from "./App";

export default function ComponentC(props) {
  const navigate = useNavigate();
  const location = useLocation();
  const match = useMatch(location.pathname);

  const [tabId, setTabId] = useState(componentCChildren[0].code);
  const handleTabChange = (e, tabId) => {
    console.log("tabId", tabId);
    navigate(`${tabId}`);
    setTabId(tabId);
  };

  return (
    <>
      <p>Component C</p>
      <TabContext value={tabId}>
        <Box sx={{ borderBottom: 1, borderColor: "divider" }}>
          <TabList onChange={handleTabChange} aria-label="lab API tabs example">
            {componentCChildren.map((tab) => {
              return <Tab key={tab.code} value={tab.code} label={tab.label} />;
            })}
          </TabList>
        </Box>
        {componentCChildren.map((tab) => {
          return (
            <TabPanel key={tab.code} value={tab.code}>
              {<tab.component />}
            </TabPanel>
          );
        })}
      </TabContext>
    </>
  );
}

这是我的沙盒的链接。

这是一个重构,它使您的大多数路由定义保持不变。 变化主要在于路线的呈现方式和位置。

应用程序.js

删除routeValues子项并将"/routeC/*"字符串文字更改为"/routeC" ,因为它同时用于路由路径链接。 渲染时将"*"通配符附加到路由的路径。

ComponentC3将使用相对链接和路径到达".../newRoute" ,其中“...”是当前匹配的路由路径。

export const ComponentC3 = () => {
  return (
    <>
      <p>Component C3</p>
      <Link to="newRoute">Take me to a new route</Link>
      <Routes>
        <Route path="newRoute" element={<SubComponentC3 />} />
      </Routes>
    </>
  );
};

export const routeValues = [
  {
    label: "Component A",
    path: "/routeA",
    component: ComponentA,
  },
  {
    label: "Component B",
    path: "/routeB",
    component: ComponentB,
  },
  {
    label: "Component C",
    path: "/routeC",
    component: ComponentC,
  }
];

export default function App() {
  return (
    <div className="App">
      <BrowserRouter>
        {routeValues.map((item) => (
          <Link key={item.path} to={item.path} style={{ paddingRight: "10px" }}>
            {item.label}
          </Link>
        ))}
        <Routes>
          {routeValues.map((route) => (
            <Route
              key={route.path}
              path={`${route.path}/*`} // <-- append wildcard '*' here
              element={<route.component />}
            />
          ))}
          <Route path="*" element={<Navigate to="routeA" />} />
        </Routes>
      </BrowserRouter>
    </div>
  );
}

组件C.js

在这里,您可以将componentCChildren渲染为后代路由。 在一个新的Routes组件中,将componentCChildren映射到Route组件,每个组件都呈现一个TabPanel组件。 再次将"*"通配符匹配器附加到路由路径,以便可以匹配更多的后代路由。 使用useEffect挂钩发出命令式重定向,从"/routeC""/routeC/subC1"的第一个选项卡。

export default function ComponentC(props) {
  const navigate = useNavigate();

  useEffect(() => {
    if (componentCChildren?.[0]?.code) {
      // redirect to first tab if it exists
      navigate(componentCChildren[0].code, { replace: true });
    }
    // run only on component mount
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const [tabId, setTabId] = useState(componentCChildren[0].code);

  const handleTabChange = (e, tabId) => {
    console.log("tabId", tabId);
    navigate(tabId, { replace: true }); // just redirect between tabs
    setTabId(tabId);
  };

  return (
    <>
      <p>Component C</p>
      <TabContext value={tabId}>
        <Box sx={{ borderBottom: 1, borderColor: "divider" }}>
          <TabList onChange={handleTabChange} aria-label="lab API tabs example">
            {componentCChildren.map((tab) => {
              return <Tab key={tab.code} value={tab.code} label={tab.label} />;
            })}
          </TabList>
        </Box>
        <Routes>
          {componentCChildren.map((tab) => {
            const TabComponent = tab.component;
            return (
              <Route
                key={tab.code}
                path={`${tab.code}/*`} // <-- append wildcard '*' here
                element={
                  <TabPanel value={tab.code}>
                    <TabComponent />
                  </TabPanel>
                }
              />
            );
          })}
        </Routes>
      </TabContext>
    </>
  );
}

编辑navigation-inside-a-child-route-not-working-properly-react-router-v6

ComponentC中,您只需要传递<Outlet /> 我更新了你的工作演示请在这里检查

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM