使用react-router的动态面包屑

IT技术 reactjs react-router
2021-04-30 09:05:47

react-router repo 的 examples 文件夹中有一个很好的例子,说明如何在现场制作面包屑但我想知道如何使用动态路由制作面包屑。

假设我们有这样的配置:

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')-> MyAwesomeProject(链接到 '/projects/11'。标题取自某处(从 store by id from routeParams 或通过其他方式))-> MyTask(链接到 '/projects/11' /999')

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

4个回答

我们使用的包装react,面包动态这是最

灵活适用于任何路由器(包括 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>
)

回购在这里:react-breadcrumbs-dynamic

有几种方法可以做到这一点,以及几种开源解决方案(也请参阅此处的答案:如何创建 react-router v4 面包屑?

就个人而言,我更喜欢 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

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

示例应用程序可在 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>);

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

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

正如@tbo 在评论中提到的,react-breadcrumbs 是一个不错的解决方案,可以用 react-router 的第 3 版来实现。您描述的动态面包屑甚至有一些解决方法(例如使用prependgetDisplayNameprops)。

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

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

更新

在找不到适合我需求的其他解决方案后,我开发了一个解决方案并在 react-breadcrumbs 存储库上打开了一个拉取请求Auto-breadcrumb,如下所述,本讨论也是潜在的解决方案,但允许较少的定制。如果您需要更多定制,请订阅并随时评论或帮助公关。

更新 (2020)

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