【问题标题】:Dynamic breadcrumbs using react router使用反应路由器的动态面包屑
【发布时间】:2016-02-01 08:59:56
【问题描述】:

有一个很好的例子来说明如何制作breadcrumbs on site in examples folder of react-router repo。但我想知道如何使用动态路线制作面包屑。

假设我们有这样的配置:

ReactDOM.render((
<Router history={browserHistory}>
    <Route path="/projects" component={ProjectsApp}>
        <IndexRoute component={ProjectsDashboard} />
        <Route path=":projectId" component={ProjectBoard}>
            <Route path=":taskId" component={ProjectTaskBoard}>
        </Route>
    </Route>
</Router>
), document.getElementById('app'));

我想做这样的事情:

Projects(链接到“/projects”)-> MyAwesomeProject(链接到“/projects/11”。标题取自某处(通过 id 来自 routeParams 或其他方式的存储))-> MyTask(链接到“/projects/11/999”)

我怎样才能达到这个结果?有没有最佳实践?谢谢。

【问题讨论】:

标签: reactjs react-router


【解决方案1】:

我们使用的包react-breadcrumbs-dynamic是最多的

灵活适用于任何路由器(包括 react-router v4)。

import {
  BreadcrumbsProvider,
  Breadcrumbs,
  BreadcrumbsItem
} from 'react-breadcrumbs-dynamic'

const theApp = (
  <BreadcrumbsProvider>
    <App />
  </BreadcrumbsProvider>
)

const App = (props) => (
  return (
    <div className="App">
      <Breadcrumbs/>
      {props.children}
    </div>
  )
}

const Page = (props) => (
  <div>
    <BreadcrumbsItem to='/'>Main Page</BreadcrumbsItem>
    {props.children}
    <Route exact path="/user" component={User} />
  </div>
)


const User = (props) => (
  <div>
    <BreadcrumbsItem to='/user'>Home</BreadcrumbsItem>
    <h2>Home</h2>
  </div>
)

repo 在这里:react-breadcrumbs-dynamic

【讨论】:

  • 面包屑项目是否会随着浏览器的前后动作自动更新
【解决方案2】:

有几种方法可以做到这一点,还有几个开源解决方案(也可以在这里查看答案:How do I create react-router v4 breadcrumbs?

就我个人而言,我更喜欢 HOC 解决方案,因为它的表面积小、渲染灵活性和可读的面包屑路由配置。

Breadcrumbs.jsx

import React from 'react';
import { NavLink } from 'react-router-dom';
import { withBreadcrumbs } from 'withBreadcrumbs';

const UserBreadcrumb = ({ match }) =>
  <span>{match.params.userId}</span>; // use match param userId to fetch/display user name

const routes = [
  { path: 'users', breadcrumb: 'Users' },
  { path: 'users/:userId', breadcrumb: UserBreadcrumb},
  { path: 'something-else', breadcrumb: ':)' },
];

const Breadcrumbs = ({ breadcrumbs }) => (
  <div>
    {breadcrumbs.map(({ breadcrumb, path, match }) => (
      <span key={path}>
        <NavLink to={match.url}>
          {breadcrumb}
        </NavLink>
        <span>/</span>
      </span>
    ))}
  </div>
);

export default withBreadcrumbs(routes)(Breadcrumbs);

withBreadcrumbs.js

import React from 'react';
import { matchPath, withRouter } from 'react-router';

const renderer = ({ breadcrumb, match }) => {
  if (typeof breadcrumb === 'function') { return breadcrumb({ match }); }
  return breadcrumb;
};

export const getBreadcrumbs = ({ routes, pathname }) => {
  const matches = [];

  pathname
    .replace(/\/$/, '')
    .split('/')
    .reduce((previous, current) => {
      const pathSection = `${previous}/${current}`;

      let breadcrumbMatch;

      routes.some(({ breadcrumb, path }) => {
        const match = matchPath(pathSection, { exact: true, path });

        if (match) {
          breadcrumbMatch = {
            breadcrumb: renderer({ breadcrumb, match }),
            path,
            match,
          };
          return true;
        }

        return false;
      });

      if (breadcrumbMatch) {
        matches.push(breadcrumbMatch);
      }

      return pathSection;
    });

  return matches;
};

export const withBreadcrumbs = routes => Component => withRouter(props => (
  <Component
    {...props}
    breadcrumbs={
      getBreadcrumbs({
        pathname: props.location.pathname,
        routes,
      })
    }
  />
));

这里也提供开源 HOC: https://github.com/icd2k3/react-router-breadcrumbs-hoc

【讨论】:

    【解决方案3】:

    这是为嵌套导航和面包屑提供单一事实来源的解决方案。

    示例应用在 GitHub 上可用:https://github.com/sneas/react-nested-routes-example

    演示:https://sneas.github.io/react-nested-routes-example/

    导航配置:

    export const navigation = [
      {
        path: "/",
        label: "All categories",
        content: () => <AllCategories />,
        routes: [
          {
            path: "/electronics",
            label: "Electronics",
            content: () => <Electronics />,
            routes: [
              {
                path: "/accessories",
                label: "Accessories",
                content: () => <Accessories />,
                routes: [
                  {
                    path: "/usb-cables",
                    label: "USB cables",
                    content: () => <UsbCables />
                  }
                ]
              },
              {
                path: "/headphones",
                label: "Headphones",
                content: () => <Headphones />
              }
            ]
          }
        ]
      }
    ];
    

    我们必须递归地展平导航并将其呈现为平面数组:

    const routes = flattenRoutes(navigation);
    return (<Router>
      {routes.map((route, index) => (
        <Route
          key={index}
          path={route.path}
          render={() => rouete.content}
        ></Route>
      ))}
    </Router>);
    

    然后用相同的导航结构构建面包屑。

    【讨论】:

      【解决方案4】:

      使用 react-router v4,所有路由都是动态的(尽管我认为仍然可以选择编写静态定义)。

      正如@tbo 在评论中提到的那样,react-breadcrumbs 是使用 react-router 版本 3 实现此功能的一个不错的解决方案。对于您描述的动态面包屑,甚至还有一些解决方法(例如,使用 prependgetDisplayName 道具)。

      in this issue 正在进行一场对话,以确定如何最好地支持 react-router v4,这也肯定与您的问题有关。我已经挖掘了一堆其他面包屑组件存储库,它们似乎都在为相同的静态路由问题而苦苦挣扎,其中一些有类似的问题需要解决 v4 支持问题。

      TLDR;如果您仍在使用 react-router v3,您可能可以使 react-breadcrumbs 包适用于动态面包屑。如果您已迁移到 v4,请继续关注,我希望很快会有更好的答案。

      更新

      在没有找到适合我需要的其他解决方案后,我开发了一个解决方案并在 react-breadcrumbs 存储库上打开了 pull requestAuto-breadcrumb,如下所述,和this discussion 也是潜在的解决方案,但允许较少的定制。如果您需要更多定制,请订阅并随时评论或提供帮助。

      更新(2020 年)

      该拉取请求已合并,react-breadcrumbs 现在适用于 react-router 版本 4 及更高版本。

      【讨论】:

      • 面包屑项目是否会随着浏览器的前后动作自动更新
      • 是的,他们会的。更新了我的答案以反映react-breadcrumbs 的当前状态。
      【解决方案5】:

      Auto-Breadcrumb 似乎是您使用 React Router 版本 4 的最佳选择。我还没有找到一个很棒的教程,但是有一个未注释但相当不错的 simple demo 可用。

      【讨论】:

        【解决方案6】:

        你可以重复使用这个组件

        import React from 'react'
        import BreadCrumbs from './BreadCrumbs/BreadCrumbs'
        
        
        
        const MainLayout = () => {
        
            return (
                <>
                    <h1>Header</h1>
                    <p><BreadCrumbs/></p>
                    <h1>Footer</h1>
                </>
            )
        }
        
        export default MainLayout
        <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

        import React from 'react'
        import { useLocation } from 'react-router-dom'
        import Breadcrumbs from '@mui/material/Breadcrumbs';
        import Link from '@mui/material/Link';
        import { Typography } from '@mui/material';
        
        const BreadCrumbs = () => {
            const location = useLocation()
            const path = location.pathname.split(/[/]/);
            console.log("location", path)
        
            return (
                <>
                    <Breadcrumbs aria-label="breadcrumb">
                        {path.map((p) => <Link underline="hover" color="inherit">{p}</Link>).slice(0, path.length - 1)}
                        {path.map((p) => <Typography color="black">{p}</Typography>).slice(path.length - 1)}
                    </Breadcrumbs>
                </>
            )
        }
        
        export default BreadCrumbs
        <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-05
          • 1970-01-01
          • 2023-04-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多