React 路由器 v4 面包屑
React router v4 breadcrumbs
我正在尝试实施 React Router Breadcrumbs for v4
以下是我的路线:
const routes = {
'/': 'Home',
'/page1': 'Page 1',
'/page2': 'Page 2'
};
我可以使用这个库在我的应用程序中放置面包屑,但是我有以下问题:
缺。 #1:
当我点击面包屑中的 Home
时,我可以看到 URL 更改为 http://localhost:8080
但是,浏览器仍然显示我所在的同一页面。
缺。 #2:
当我从第 1 页导航到第 2 页时,url
从 http://localhost:8080/page1
变为 http://localhost:8080/page2
。
因此显示的面包屑更改为 Home / Page 2
而不是像 Home / Page 1 / Page 2
我知道这可能是因为 url
主机名后面只有 /page2
。但是,我可以实现这样的显示吗:Home / Page 1 / Page 2
?
下面是我主要的代码 App.jsx
:
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Route exact path="/" component={LandingPage}/>
<Route path="/page1" component={Page1}/>
<Route path="/page2" component={Page2}/>
</div>
</Router>
如果我像下面那样使用面包屑,那么我的第 2 页会呈现在第 1 页的下面:
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Route exact path="/" component={LandingPage}/>
<Route path="/page1" component={Page1}/>
<Route path="/page1/page2" component={Page2}/>
</div>
</Router>
答案:
缺。 #1: 无需在应用程序的每个组件内将 <Breadcrumbs ..../>
元素包装在 <Router>
元素内。这可能是因为,在每个组件中包含 <Router>
元素会导致 "nesting" 个 Router
元素(注意我们在着陆页中也有 Router
标记);这不适用于 react router v4
。
缺。 #2:参考这里正式标注的回答(下面palsrealm回答)
您的面包屑导航基于链接,它们按设计工作。要显示页面,您需要设置一个 Switch
并在其中设置 Route
s 以在路径更改时加载适当的组件。像
<Switch>
<Route path='/' component={Home}/>
<Route path='/page1' component={Page1}/>
<Route path='/page2' component={Page2}/>
</Switch>
如果您希望面包屑显示 Home/Page1/Page2
,您的 routes
应该是 '/page1/page2' : 'Page 2'
。 Route
也应相应更改。
编辑:您的 Router
应该是
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Switch>
<Route exact path="/" component={LandingPage}/>
<Route exact path="/page1" component={Page1}/>
<Route path="/page1/page2" component={Page2}/>
</Switch>
</div>
</Router>
这也可以通过 HOC 来完成,它允许您使用路由配置对象来设置面包屑。我已经开源了here,但是源代码也在下面:
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,
})
}
/>
));
以下组件应 return 任何深度的面包屑导航,但主页除外(出于显而易见的原因)。你不需要 React Router Breadcrumb。我的第一个 public 贡献,所以如果我遗漏了一个重要的部分,如果有人能指出它就太好了。我为面包屑分割添加了 »
,但您显然可以更新它以匹配您的需要。
import React from 'react'
import ReactDOM from 'react-dom'
import { Route, Link } from 'react-router-dom'
// styles
require('./styles/_breadcrumbs.scss')
// replace underscores with spaces in path names
const formatLeafName = leaf => leaf.replace('_', ' ')
// create a path based on the leaf position in the branch
const formatPath = (branch, index) => branch.slice(0, index + 1).join('/')
// output the individual breadcrumb links
const BreadCrumb = props => {
const { leaf, index, branch } = props,
leafPath = formatPath(branch, index),
leafName = index == 0 ? 'home' : formatLeafName(leaf),
leafItem =
index + 1 < branch.length
? <li className="breadcrumbs__crumb">
<Link to={leafPath}>{leafName}</Link>
<span className="separator">»</span>
</li>
: <li className="breadcrumbs__crumb">{leafName}</li>
// the slug doesn't need a link or a separator, so we output just the leaf name
return leafItem
}
const BreadCrumbList = props => {
const path = props.match.url,
listItems =
// make sure we're not home (home return '/' on url)
path.length > 1
&& path
// create an array of leaf names
.split('/')
// send our new array to BreadCrumb for formating
.map((leaf, index, branch) =>
<BreadCrumb leaf={leaf} index={index} branch={branch} key={index} />
)
// listItem will exist anywhere but home
return listItems && <ul className="breadcrumbs">{listItems}</ul>
}
const BreadCrumbs = props =>
<Route path="/*" render={({ match }) => <BreadCrumbList match={match} />} />
export default BreadCrumbs
我正在尝试实施 React Router Breadcrumbs for v4
以下是我的路线:
const routes = {
'/': 'Home',
'/page1': 'Page 1',
'/page2': 'Page 2'
};
我可以使用这个库在我的应用程序中放置面包屑,但是我有以下问题:
缺。 #1:
当我点击面包屑中的 Home
时,我可以看到 URL 更改为 http://localhost:8080
但是,浏览器仍然显示我所在的同一页面。
缺。 #2:
当我从第 1 页导航到第 2 页时,url
从 http://localhost:8080/page1
变为 http://localhost:8080/page2
。
因此显示的面包屑更改为 Home / Page 2
而不是像 Home / Page 1 / Page 2
我知道这可能是因为 url
主机名后面只有 /page2
。但是,我可以实现这样的显示吗:Home / Page 1 / Page 2
?
下面是我主要的代码 App.jsx
:
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Route exact path="/" component={LandingPage}/>
<Route path="/page1" component={Page1}/>
<Route path="/page2" component={Page2}/>
</div>
</Router>
如果我像下面那样使用面包屑,那么我的第 2 页会呈现在第 1 页的下面:
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Route exact path="/" component={LandingPage}/>
<Route path="/page1" component={Page1}/>
<Route path="/page1/page2" component={Page2}/>
</div>
</Router>
答案:
缺。 #1: 无需在应用程序的每个组件内将 <Breadcrumbs ..../>
元素包装在 <Router>
元素内。这可能是因为,在每个组件中包含 <Router>
元素会导致 "nesting" 个 Router
元素(注意我们在着陆页中也有 Router
标记);这不适用于 react router v4
。
缺。 #2:参考这里正式标注的回答(下面palsrealm回答)
您的面包屑导航基于链接,它们按设计工作。要显示页面,您需要设置一个 Switch
并在其中设置 Route
s 以在路径更改时加载适当的组件。像
<Switch>
<Route path='/' component={Home}/>
<Route path='/page1' component={Page1}/>
<Route path='/page2' component={Page2}/>
</Switch>
如果您希望面包屑显示 Home/Page1/Page2
,您的 routes
应该是 '/page1/page2' : 'Page 2'
。 Route
也应相应更改。
编辑:您的 Router
应该是
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Switch>
<Route exact path="/" component={LandingPage}/>
<Route exact path="/page1" component={Page1}/>
<Route path="/page1/page2" component={Page2}/>
</Switch>
</div>
</Router>
这也可以通过 HOC 来完成,它允许您使用路由配置对象来设置面包屑。我已经开源了here,但是源代码也在下面:
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,
})
}
/>
));
以下组件应 return 任何深度的面包屑导航,但主页除外(出于显而易见的原因)。你不需要 React Router Breadcrumb。我的第一个 public 贡献,所以如果我遗漏了一个重要的部分,如果有人能指出它就太好了。我为面包屑分割添加了 »
,但您显然可以更新它以匹配您的需要。
import React from 'react'
import ReactDOM from 'react-dom'
import { Route, Link } from 'react-router-dom'
// styles
require('./styles/_breadcrumbs.scss')
// replace underscores with spaces in path names
const formatLeafName = leaf => leaf.replace('_', ' ')
// create a path based on the leaf position in the branch
const formatPath = (branch, index) => branch.slice(0, index + 1).join('/')
// output the individual breadcrumb links
const BreadCrumb = props => {
const { leaf, index, branch } = props,
leafPath = formatPath(branch, index),
leafName = index == 0 ? 'home' : formatLeafName(leaf),
leafItem =
index + 1 < branch.length
? <li className="breadcrumbs__crumb">
<Link to={leafPath}>{leafName}</Link>
<span className="separator">»</span>
</li>
: <li className="breadcrumbs__crumb">{leafName}</li>
// the slug doesn't need a link or a separator, so we output just the leaf name
return leafItem
}
const BreadCrumbList = props => {
const path = props.match.url,
listItems =
// make sure we're not home (home return '/' on url)
path.length > 1
&& path
// create an array of leaf names
.split('/')
// send our new array to BreadCrumb for formating
.map((leaf, index, branch) =>
<BreadCrumb leaf={leaf} index={index} branch={branch} key={index} />
)
// listItem will exist anywhere but home
return listItems && <ul className="breadcrumbs">{listItems}</ul>
}
const BreadCrumbs = props =>
<Route path="/*" render={({ match }) => <BreadCrumbList match={match} />} />
export default BreadCrumbs