2
votes

I'm have a Redux app that uses react router with nested routes, like this:

<Provider store={store}>
    <Router history={browserHistory}>
        <Route name="sales" path="/" component={App}>
          <IndexRoute name="home" component={Home} />
          <Route name="reports" path="/reports" component={Reports}>
            <IndexRoute name="reports-home" component={ReportsHome} />
          <Route name="report-1" path="/reports/report-1" component={Report1}/>
          <Route name="report-2" path="/reports/report-2" component={Report2}/>
          </Route>
        </Route>
    </Router>
</Provider>

I'm trying to write a breadcrumbs component; so would like to be able to deterime the current route.

I've configured the component to receive the router using the withRouter function provided by react router:

Breadcrumbs = withRouter(Breadcrumbs);

This gives me a router object that looks like this:

Object {__v2_compatible__: true}
__v2_compatible__: true
createHref: createHref(location, query)
createKey: createKey()createLocation: createLocation(location)
createPath: createPath(location, query)
go: go(n)
goBack: goBack()
goForward: goForward()
isActive: isActive(location)
listen: listen(listener)
listenBefore: listenBefore(hook)
push: push(location)
pushState: ()
registerTransitionHook: registerTransitionHook(hook)
replace: replace(location)
replaceState: ()
setRouteLeaveHook: listenBeforeLeavingRoute(route, hook)
setState: ()
transitionTo: transitionTo(nextLocation)
unregisterTransitionHook: unregisterTransitionHook(hook)
__proto__: Object

Can I use this to determine the current route? Is there better way?

2
isActive can be used to determine if a location pathname is active or notDeadfish

2 Answers

1
votes

Getting location etc. via withRouter was added in react-router version 3.0. Dan Abramov recommends upgrading to 3.0 to use withRouter. From 2.7 to 3.0, it only provided the functions you saw.

Source: https://github.com/ReactTraining/react-router/blob/master/CHANGES.md#v300-alpha1

0
votes

There is already a module that does this for you, I believe it's called react-router-breadcrumbs. I haven't tried it though.

If you want a custom solution, here's what you could do:


Use the this.props.routes and this.props.params objects. You can then map through the routes and for each entry make a lookup for such key in the params object. You can then create a string with said parameters.

Note that I have given each route (except IndexRoutes) a path attribute, because sometimes I want to display a custom name for a given page. For example:

<Route path="/:productId" name="Start" title="Start" component={StartView} />

Here's the solution on my app:

componentDidMount = () => {
  this._prepareBreadCrumbs(this.props);
}

componentWillReceiveProps = (newProps) => {
  this._prepareBreadCrumbs(newProps);
}

_prepareBreadCrumbs = (props) => {
  let {routes, params} = props;
  let breadcrumbPath = "";
  let temp = routes.map(
    (item, i) => {
      if(item.path == null) return null; //if we are visiting an item without a path, ignore it.
      if(i < routes.length-1 && routes[i+1].path != null) {
        let arr = item.path.split(/[:/]|(:\/)/g); //sometimes the path is like "/:product_id/details" so I need to extract the interesting part here.
        arr = arr.map(function(obj) {
          return (obj in params) ? params[obj] : obj; //We now have ":product_id" and "details" - the first one will exist in the "params" object.
        });
        breadcrumbPath += arr.filter(Boolean).join("/") + "/";  //clean out some garbage and add the "/" between paths.
        if(i == 0) return <li key={i}><Link to={breadcrumbPath}>YourSite.com</Link></li>  //If on the root - display the site name
        return <li key={i}><Link to={breadcrumbPath}>{item.name}</Link></li>
      } else {
        document.title = "YourSite.com - " + item.title;  //set the document title if you want
        if(i == 0) return <li key={i} className="active"><span>YourSite.com</span></li>
        return <li key={i} className="active"><span>{item.name}</span></li>
      }
    }
  );
  this.setState({breadcrumbPath: temp});
}

render() {
  <p>{this.state.breadCrumbPath || ""}</p>
}

You'd want to put this in your top-level React Component.