First of all, thanks for working on React Router v4. I like the changes applied in the new version, really clean API, really well done.
I work on Grommet, an UX framework that runs on top of ReactJS and supports React Router.
Recently I tried to start our migration to support react router v4. In the past we used to be completely un-opinionated about routing. But recently we added support for React Router inside our Anchor component. We do not use Link or NavLink, we use a prop called path
to identify when to use React router and when not to use it.
React router Anchor: <Anchor path="/test" icon={<PreviousIcon />} />
Anchor without React Router: <Anchor href="/test" icon={<PreviousIcon />} />
I'm calling history.push
and history.replace
in a very similar way that React Router <Link />
does.
The problem I'm facing with v4 is that this.context.router.isActive
is completely gone. I tried to find a reason why, or some migration guide, without success. Here is our current implementation of the Anchor that uses React v3:
https://github.com/grommet/grommet/blob/master/src/js/components/Anchor.js#L23
Well, I tried looking into the new <NavLink />
implementation and it seems now things are very JSX-driven. From what I understood from this component it seems that you are declaring a new <Route />
to be able to get a match in the callback. Initially this seems odd to me as I'm not creating a new Route but trying to identify if a given path is active or not.
Could you please explain why router.isActive
is gone and what would be a way to get the active route without using JSX for that?
Rendering a <Route>
does not necessarily mean "only render when you match the current location". For example, it can be used inject the router variables from the context into a component as props.
In <NavLink>
, the <Route>
is using the children
prop, which means that it will call the children
function whether or not the route matches. If the match is null
, then we know that it did not match.
If you would prefer not to use <Route children>
in this way, React Router offers an imperative approach with the matchPath
function. This is what <Switch>
es and <Route>
s use internally to match a location against a path.
Thanks let me try that right now 馃憤 appreciate the explanation.
I tried but it is always returning the root path.
As you can see when the path is /dashboard
I would expect the return to be that, and not /
.
Here is how I'm describing the Routes:
<Router>
<App centered={false}>
<Split priority={priority} flex='right'
onResponsive={this._onResponsive}>
{nav}
<div>
<Route exact={true} path='/' component={Dashboard} />
<Route path='/login' component={Login} />
<Route path='/dashboard' component={Dashboard} />
<Route exact={true} path='/tasks' component={Tasks} />
<Route path='/tasks/:id' component={Task} />
</div>
</Split>
</App>
</Router>
By inspecting how matchPath
works, it expects you to send the path
as an option, otherwise it will use /
by default.
In v3 isActive
module receives the existing routes:
https://github.com/ReactTraining/react-router/blob/v3/modules/isActive.js#L125
and I don't think matchPath
does that.
I'm not actually sure where you are logging.
Anyways, this is the matchPath
implementation I was referring to. The important thing to remember is that partial matches will be "active", so if you only want the path to be "active" when it completely matches, you will need to pass the exact: true
option to matchPath
or check the returned match object's isExact
property.
let active = false
const { router } = this.context
const { path } = this.props
if (path && router) {
const { location } = router
active = matchPath(location.pathname, { path }) != null
}
this.setState({ active })
Thanks this works!
using context is usually not a good idea I am having this problem right now and kinda found a work around . i'll paste some code here .
export default () => (
<Router>
<div>
<Route path="/:active?" component={Nav} />
<div className="container">
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</div>
</div>
</Router>
)
here I am rendering nav
on every every path basically and giving it a nameactive
and making it optional for home
now I can check for match.params.active
in nav component
it gives the full path .
heres an example
<div className="nav navbar-nav" >
<li className={!match || !match.params || !match.params.active ? "active" : ""}><NavLink to="/">Home</NavLink></li>
<li className={match.params.active === 'about' ? "active" : ""}><NavLink to="/about">About</NavLink></li>
<li className={match.params.active === 'notfound' ? "active" : ""}><NavLink to="/notfound">nothing</NavLink></li>
</div>
Is this relevant to you?
https://reacttraining.com/react-router/web/api/NavLink
See also the implementation:
https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/NavLink.js
Most helpful comment
Is this relevant to you?
https://reacttraining.com/react-router/web/api/NavLink
See also the implementation:
https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/NavLink.js