How to have react route path one/two/three? - react-router

I would like to have something like this
<Route
path="/one/two/three"
render={() => (
<Component/>
)}
exact
/>
But it does not seem to work locally when I wrap it all into a Router. I only see the contents of the Component when it is just /one, but I would like to have /one/two/three as the path (being my root - user lands on that path).

In react-router-v4 you can have below routes only,
<Route path="/one" render={() => (<Component/>)} exact /> //Direct route without any params
or
<Route path="/one/:two" render={() => (<Component/>)} exact /> //Route with params, here `:two` is the parameter to route
If you want to use routes like in your example, then this can be achieve using BrowseRouter basename attribute,
<BrowserRouter basename="/one/two">
<Route path="/three" render={() => (<Component/>)} exact />
</BrowserRouter>
And you Link should be like,
<Link to="/three"/> // renders <a href="/one/two/three">

Few reasons why this did not work for me.
I was missing output: { publicPath: '/' }, in my webpack config.
And weirder thing was that I was using PureComponent instead of Component which totally broke the rending when redirecting to the next step with similar path(x/y/z) or using Link.

Related

React (5): Make route navigate to an external link

I'm trying to make react navigate to an external link from a route. I don't feel like adding an restyling the header.
<Switch>
<Route exact path='/'>
<PageLayout>
<LandingPage />
</PageLayout>
</Route>
<Route exact path='/example'>
<a href="www.example.com" />
</Route>
</Switch>
I'm just looking for the simplest way to do this. I don't want to have to restyle the header.
Preferably it would open up a new page.
Edit I've also tried
<Route exact path='/example'>
<Redirect to='https://www.example.com' />
</Route>
react-router-dom only deals with internal routing & navigation within a React app. If you want are trying to navigate/redirect to a URL that is external to your app from a matched route then I suggest using window.open and open in a new browser context, like a new window or tab. You can create a custom component to do this as a mounting effect.
Example:
import { useHistory } from 'react-router-dom';
const RedirectExternal = ({ to }) => {
const history = useHistory();
React.useEffect(() => {
window.open(to, "_blank", "noreferrer");
// use timeout to move back navigation to end of event queue
setTimeout(history.goBack);
}, [history, to]);
return null;
};
Usage:
<Link to="/example">www.example.com</Link>
...
<Switch>
<RedirectExternal from="/example" to="https://www.example.com" />
<Route path="/">
<PageLayout>
<LandingPage />
</PageLayout>
</Route>
</Switch>
It might just be easier to link to the external page directly though.
<a href="https://www.example.com" rel="noreferrer" target="_blank">
www.example.com
</a>
Since you are using react-router-dom, you could do the following to achieve an external link in navigation.
<Route
path="/myPath"
component={() => {
if (window) {
window.open(
"https://www.google.com"
);
}
return null;
}}
/>

Issue with connected-react-router

I have created the following routes using connected-react-router as shown below
<Switch>
<Route exact={true} path="/a" component={A}/>
<Route exact={true} path="/b" component={B}/>
<Route path="/c/:id" component={C}/>
</Switch>
But when I hit the url my.domain.com/c, the component does not get render. But when I go and give the url my.domain.com/c/12, then it works. I have tried setting exact={false} as well. Still it does not work. Any help will help me proceed further.
When you declare the route:
<Route path="/c/:id" component={C}/>
You are saying that the "id" it's mandatory. If you need render the page even if the user don't pass the id, you need to add "?":
<Route path="/c/:id?" component={C}/>

react-router render menu when path does not match

I'm using react-router and I want to render a menu component when the user is not in the root and not in the /login path. This is what I have so far
<Route path="/:subpath" component={TopMenuComponent} />
<div>
<Route
exact path="/"
render={props => (
<LoginContainer {...props} setTitle={this.setTitle} />
)}
/>
<Route path='/landing' component={LandingComponent} />
</div>
takes care of not rendering the TopMenuComponent component in the '/' location, however how do I avoid it rendering TopMenuComponent when the user is in the /login path? I could always create another component and wrap it up, but I think that is too much just for this.
Simplest Implementation
Use a ternary expression or short-circuit evaluation to conditionally render your component based on location.pathname, like so:
<Route
render={({ location }) => ['/', '/login'].includes(location.pathname)
? <Component/>
: null
}
/>
Regex Implementation
React Router's matching of path strings relies on path-to-regexp#^1.7.0.
As a result, you can instruct routes to not render for certain paths using regular expressions.
The following implementations should render given any path value, bar "/" and "/login":
// With Regex Inside String.
<Route path={"^(?!.*(\/|\/login)).*$"} component={TopMenuComponent}/>
// With Explicit Regex.
<Route path={new RegExp('^(?!.*(\/|\/login)).*$')} component={TopMenuComponent}/>
Regex in the route path didn't work for me. What worked for me was this. Just add the other condition.
<Route render={({ location }) => {
return location.pathname.indexOf('/login') === -1 ? TopMenuComponent : null
}} />
If you don't wish to use Regular Expressions directly, you can place your login Route in a Switch with the top menu component Route. It will only run the first matching Route and routes without a path attribute match anything.
<div>
<Switch>
<Route
exact path="/"
render={props => (
<LoginContainer {...props} setTitle={this.setTitle} />
)}
/>
<Route path="/:subpath" component={TopMenuComponent} />
</Switch>
<Route path='/landing' component={LandingComponent} />
</div>
For your example, you would need to reorder your divs.
Taken Regex from Arman's answer.
const notInLogin = /^(?!.*(\/login)).*$/
export default () => (
<Router history={history}>
<>
<Route path={notInLogin} component={NavBar} />
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/accounts" component={Account} />
<Route exact path="/profile" component={Profile} />
<Route exact path="/" component={Home} />
</Switch>
</>
</Router>
)
If you get PropsType error: https://stackoverflow.com/a/50439120/1099314
Similar to taylor michels answer, but the following accounts for both the '/login' and the '/' (root) routes:
<Route
render={({ location }) =>
location.pathname !== "/" && location.pathname !== "/login" ? (
<TopMenuComponent />
) : null
}
/>>
This also renders the component as a jsx tag <TopMenuComponent />, which works for me where the other approach did not.
You can use useRouteMatch hook
const ParentComponent = props => {
const matched = useRouteMatch(['/', '/login'])
if (matched && matched.isExact) return null
return <ChildComponent {...props} />
}

In react router 4, how does one negate a route / path using regex?

Say i have a route switch statement like the following:
render () {
<pre>
<Switch>
<Route path="/foo" render={render}>
<Route path="/bar" render={renderBar}>
<Route path="/" render={renderHome}>
{/* How do i express everything except the home page ?*/}
<Route render={renderFourOhFour}>
</Switch>
</pre>
}
How do i write a route that excludes everything except the home page given the above example? Do i just write a regex? If so i've tried something like
path={^(?!.*(home))}
with the regex react router v4 tester: https://pshrmn.github.io/route-tester/#/
You can use the render method on the Route, which gets passed the location as a prop. So:
<Route render={({location}) => {
return location.pathname !== '/' ? <p>Not home</p> : ''
}} />
1) It will be visible everywhere except /home
<Route path={/\/(?!home)/} component={Component} />
2) Everywhere except /
<Route path={/^.{2,}$/} component={Component} />
In react router 4, there isn't a explicit way to. I had redesign it in a way that the switch statement as a stack or queue.
It'll match the first few route components as the first choices and you'll have to place the last item as the default.
For example:
<Route path="/" exact component={Home}/>
<Route path="/will-match" component={WillMatch}/>
<Route component={NoMatch} />

react router not updating browser's location bar

I met a weird problem here, I am using a simple router set up like:
<Provider store={store}>
<div>
<Router
location='history'
history={createHistory({queryKey: false})}
onUpdate={() => window.scrollTo(0, 0)}>
<Route path="/" component={Main}>
<Route path="datacenter/:name" component={App} />
<Route path="operation" component={OperationComponent} />
</Route>
</Router>
<DevTools />
</div>
</Provider>
and I have a nav list that manipulates the History:
<SelectableList subheader='OPERATIONS' valueLink={{value: this.props.location.pathname, requestChange: this.operationChanged}}>
<ListItem primaryText='Checks' value='/operation' onTouchTap={this.handleClose}/>
</SelectableList>
with action callback:
operationChanged = (evt, val) => {
this.props.history.push(val);
};
the problem is the navigation is working, when I click one of the list item, I am navigated to the path as expected, however, the selected path is not being updated to browser's address bar ...
Any ideas?
Thanks in advance
UPDATE *********
Turns out, it is an issue related to webpack-dev-server, while using this dev tool, the react-router failed to update browser's location bar ... did not figure out why though
Could be because on the Router you are setting:
location='history'
Try removing that line, also a JSBin or plnkr would be helpful so I could play around with it!
check out the docs here and follow the basic setup: Check out the bottom of the second example!