I am trying to implement React Router Breadcrumbs for v4
Following are my routes:
const routes = {
'/': 'Home',
'/page1': 'Page 1',
'/page2': 'Page 2'
};
I could put the breadcrumbs using this library in my application, however I am having following questions:
Que. #1:
When I click on Home
in my breadcrumbs, I can see the URL changes to http://localhost:8080
However, browser still shows the same page I am on.
Que. #2:
When I navigate to Page2 from Page1, url
changes from http://localhost:8080/page1
to http://localhost:8080/page2
.
So the breadcrumbs shown changes to Home / Page 2
instead of changing like Home / Page 1 / Page 2
I know this may be because the url
just has /page2
after hostname. But, can I achieve the display like: Home / Page 1 / Page 2
?
Below is the code in my main App.jsx
:
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Route exact path="/" ponent={LandingPage}/>
<Route path="/page1" ponent={Page1}/>
<Route path="/page2" ponent={Page2}/>
</div>
</Router>
and if I use like belowto cater for breadcrumbs, then my page2 gets rendered below page1 stuff:
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Route exact path="/" ponent={LandingPage}/>
<Route path="/page1" ponent={Page1}/>
<Route path="/page1/page2" ponent={Page2}/>
</div>
</Router>
Answer:
Que. #1: No need to wrap <Breadcrumbs ..../>
element inside <Router>
element inside each Component of application. This may be because, inclusion of <Router>
element inside each Component leads to "nesting" of Router
elements (note we have Router
tag in landing page as well); which does not work with react router v4
.
Que. #2: Refer to answer formally marked here (answered by palsrealm below)
I am trying to implement React Router Breadcrumbs for v4
Following are my routes:
const routes = {
'/': 'Home',
'/page1': 'Page 1',
'/page2': 'Page 2'
};
I could put the breadcrumbs using this library in my application, however I am having following questions:
Que. #1:
When I click on Home
in my breadcrumbs, I can see the URL changes to http://localhost:8080
However, browser still shows the same page I am on.
Que. #2:
When I navigate to Page2 from Page1, url
changes from http://localhost:8080/page1
to http://localhost:8080/page2
.
So the breadcrumbs shown changes to Home / Page 2
instead of changing like Home / Page 1 / Page 2
I know this may be because the url
just has /page2
after hostname. But, can I achieve the display like: Home / Page 1 / Page 2
?
Below is the code in my main App.jsx
:
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Route exact path="/" ponent={LandingPage}/>
<Route path="/page1" ponent={Page1}/>
<Route path="/page2" ponent={Page2}/>
</div>
</Router>
and if I use like belowto cater for breadcrumbs, then my page2 gets rendered below page1 stuff:
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Route exact path="/" ponent={LandingPage}/>
<Route path="/page1" ponent={Page1}/>
<Route path="/page1/page2" ponent={Page2}/>
</div>
</Router>
Answer:
Que. #1: No need to wrap <Breadcrumbs ..../>
element inside <Router>
element inside each Component of application. This may be because, inclusion of <Router>
element inside each Component leads to "nesting" of Router
elements (note we have Router
tag in landing page as well); which does not work with react router v4
.
Que. #2: Refer to answer formally marked here (answered by palsrealm below)
Share Improve this question edited Oct 11, 2017 at 19:10 Akshay Lokur asked Oct 11, 2017 at 12:47 Akshay LokurAkshay Lokur 7,52615 gold badges48 silver badges69 bronze badges 2- any react codes? – Rei Dien Commented Oct 11, 2017 at 13:48
- Its same as the URL given in question, thanks – Akshay Lokur Commented Oct 11, 2017 at 15:40
3 Answers
Reset to default 2Your breadcrumbs are based on links and they work as designed. To display the pages, you need to set up a Switch
with Route
s in it which would load the appropriate ponents when the path changes. Something like
<Switch>
<Route path='/' ponent={Home}/>
<Route path='/page1' ponent={Page1}/>
<Route path='/page2' ponent={Page2}/>
</Switch>
If you want the breadcrumb to show Home/Page1/Page2
your routes
should be '/page1/page2' : 'Page 2'
. The Route
should also change accordingly.
Edit: Your Router
should be
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Switch>
<Route exact path="/" ponent={LandingPage}/>
<Route exact path="/page1" ponent={Page1}/>
<Route path="/page1/page2" ponent={Page2}/>
</Switch>
</div>
</Router>
This can also be acplished with a HOC which would allow you to use a route config object to set breadcrumbs instead. I've open-sourced it here, but the source code is below as well:
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,
})
}
/>
));
The following ponent should return a breadcrumb at any depth, except on the home page (for obvious reasons). You won't need React Router Breadcrumb. My first public contribution, so if I'm missing an essential part, it would be great if somebody could point it out. I added »
for crumbs splits, but you can obviously update that to match what you need.
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