How to match Route to any path EXCEPT the base (home) path? - javascript

I've enjoyed using react-router to conditionally render based on location. For example, I have a component that only renders if I am on the home page, like so:
<Route
exact={ true }
path="/"
render={ HomeHeroSection }
/>
However, because of design requirements, I also have a component that should only be rendered if NOT at the home page/base path. I have only been able to come up with a path-to-regexp pattern that accomplishes this if I name some arbitrary unused parameter, as a sort of wildcard.
<Route
path="/:foo+"
render={ NavLinks }
/>
Is there a better pattern for the path prop that I should be using? Or perhaps a better general react-router paradigm? I've considered Switch, among other things, but I'm curious if I am missing something simple.

If you want the HomeHeroSection component to render only on the root route and the NavLinks component to render only on any other route, this is the use case for Switch. From the docs on Switch:
Switch is unique in that it renders a route exclusively. In contrast, every that matches the location renders inclusively.
So Switch is the right call here.
Here's how you can use it in this case:
<Switch>
<Route exact path='/' component={ HomeHeroSection }/>
<Route component={ NavLinks }/>
</Switch>
Note that the order of the Route components is important here as Switch renders the first child Route that matches the location.
Note that not providing a path attribute to a Route will cause it to match any route (so these should go last in order). If the location that was used to arrive at the NavLinks component matters, you can include the path attribute like normal.
Edit:
Since the two components do not appear in the same place on the page you can use just a plain Route for the HomeHeroSection as you have already, and a Switch that renders null on the root route for the NavLinks component. Here's an example:
<main>
<Route exact path='/' component={ HomeHeroSection }/>
<span>Some other stuff</span>
<Switch>
<Route exact path='/' render={ null }/>
<Route component={ NavLinks }/>
</Switch>
</main>
This will render the NavLinks component for any route other than the root and will render nothing on the root route.

Related

React Router - 404 page always showing due to custom route component

I have a component which takes in similar props to the standard react-router Route and returns a route. This returned route has a render prop which just passes through the component but adds a nav component. This custom NavRoute does not work with the react-router Switch component, as when I use the custom component inside the switch along with a catch-all 404 page, the page always shows.
How can I make the 404 component only show if none of the other routes match the url?
NavRoute
const NavRoute = ({ exact, path, component: Component }) => {
return (
<Route exact={exact} path={path} render={(props) => (
<div style={styleSideNav}>
<Nav />
<Component {...props} />
</div>
)}/>
);
});
Usage
<Switch>
<NavRoute exact path="/" component={Home} />
<NavRoute exact path="/about" component={About} />
<Route path="/" component={Page404} />
</Switch>
EDIT:
Codesandbox of full example: https://codesandbox.io/s/react-router-stack-overflow-5jcum
Url of codesandobx: https://5jcum.csb.app/invite/abc
Switch only works with the first level of components directly under it. It can't traverse the entire tree.
https://github.com/ReactTraining/react-router/issues/5785#issuecomment-351067856
To fix that, we can pass array of routes instead of routes wrapped in fragment. I have made the changes and updated in codesandbox.
https://codesandbox.io/s/react-router-stack-overflow-hu62w?file=/src/App.js

Navbar causing other components to not load in React Router

I am setting up a basic navbar, it just has some buttons, not any complex log or anything. When I have this navbar inside of the switch, no other componet loads, meaning I cannot wrap the navbar between certain components, when I have the navbar outside of the switch , then it works, and I can just check if props.match.location etc is "/" and not load it on homepage, so I found a workaround, but what could be causing this bug, or is it expected behavious, I suspect my wildroutes, but not 100% sure what .
Where I am using navbar
const App = () => {
return (
<div>
<Router>
<Switch>
<Route component={theNavbar} />
<Route exact path="/" component={Home} />
<Route exact path="/api/:city/electronics" component={Electronics} />
<Route exact path="/api/:city/labour" component={Labour} />
<Route exact path="/api/posts/item/:id" component={ItemDetails} />
<Route exact path="/create/:city/:category" component={CreatePost} />
</Switch>
</Router>
</div>
);
};
It's completely expected behavior, and caused by a couple of factors. First is the Switch, and the second is the match anything Route for the nav.
Switch
Renders the first child <Route> or <Redirect> that matches the
location.
All subsequent matches won't render at all.
Route path
Routes without a path always match.
<Route component={theNavbar} />
This route matches all locations and thus will always match. Being listed first within a Switch means it will always render.
The solution to always render some navigation component while still matching other routes is to move that component outside of the Switch but still within the Router.
This is expected behavior. Per the React Router documentation:
Routes without a path always match.
So your NavBar will always be matched in the <Switch> and none of the other components will render.

using same component for different route path in react-router v4

I am trying to have separate routes but same component for add/edit forms in my react app like the below:
<Switch>
<Route exact path="/dashboard" component={Dashboard}></Route>
<Route exact path="/clients" component={Clients}></Route>
<Route exact path="/add-client" component={manageClient}></Route>
<Route exact path="/edit-client" component={manageClient}></Route>
<Route component={ NotFound } />
</Switch>
Now in the manageClient component, I parse the query params (I pass in a query string with client id in edit route), I render conditionally based on the query param passed.
The problem is that this doesn't remount the whole component again. Say an edit page is opened, and the user clicks on add component, the URL changes, but the component doesn't reload and hence remains on the edit page.
Is there a way to handle this?
Using different key for each route should force components to rebuild:
<Route
key="add-client"
exact path="/add-client"
component={manageClient}
/>
<Route
key="edit-client"
exact path="/edit-client"
component={manageClient}
/>
One solution is use inline function with component, that will render a new component each time, But this is not a good idea.
Like this:
<Route exact path="/add-client" component={props => <ManageClient {...props} />}></Route>
<Route exact path="/edit-client" component={props => <ManageClient {...props} />}></Route>
Better solution would be, use componentWillReceiveProps lifecycle method in ManageClient component. Idea is whenever we render same component for two routes and switching between them, then react will not unmount-mount component, it will basically update the component only. So if you are making any api call or require some data do all in this method on route change.
To check, use this code and see it will get called on route change.
componentWillReceiveProps(nextProps){
console.log('route chnaged')
}
Note: Put the condition and make the api call only when route changes.
<Route exact path={["/add-client", "/edit-client"]}>
<manageClient />
</Route>
Reference
Version 5.2.0
https://reacttraining.com/react-router/web/api/Route/path-string-string
My problem was we used an common path in-between, which causes dynamic path to not working
<Switch>
<Route key="Home" path="/home" component={Home} />
<Route key="PolicyPlan-create" path="/PolicyPlan/create" component={PolicyPlanCreatePage} />
{/* <Route key="PolicyPlan-list" path="/PolicyPlan" component={PolicyPlanListPage} /> */}
<Route key="PolicyPlan-list" path="/PolicyPlan/list" component={PolicyPlanListPage} />
<Route key="PolicyPlan-edit" path="/PolicyPlan/edit/:id" component={PolicyPlanCreatePage} />
<Route key="cardDesign" path="/cardDesign" component={cardDesign} />
<Route key="Admin-create" path="/admin/create" component={RegisterPage} />
</Switch>
So don't use the path like the commented one, now the code is working
.................
this.props.history.push("/PolicyPlan/edit/" + row.PolicyPlanId);
.............
You can simply provide an array of paths in a single route tag as follows -
<Route exact path={["/add-client", "/edit-client"]} component={manageClient}></Route>

React router call the same code in each route component

I have the next routes on main component of my app:
<div>
<h1><Link to="/">Home</Link></h1>
<Switch>
<Route exact path="/" component={FirstPage} />
<Route exact path="/:language?/second" component={SecondPage} />
<Route exact path="/:language?/account" component={AccountPage} />
<Route exact path="/:language?/add" component={AddNewPage} />
<Route component={NoMatch} />
</Switch>
</div>
I need to add in each child component checking this.props.match.params.language (this is react-router props) in order to set the current language. But white this code in each componentWiilMount looks wired on my mind. Even I put this checking in a single function and will call it every componentWiilMount and pass this.props.match.params.language props to it, anyway it a mess of code. For example, if I have 100 routes I need to add this checking 100 times.
Also, I think about adding this code to the main component lifecycle, and it will be called when the page changed, but I do not have react-router props here.
Maybe you know a better solution for this?
You may want to try nesting your routes. You could have one parent route component that manages the language then nests child route components:
function Root() {
return (
<div>
<h1><Link to="/">Home</Link></h1>
<Route path="/:language?" component={LanguageSelector}/>
</div>
);
}
class LanguageSelector extends React.Component {
componentDidMount() {
// Manage language (if specified)
}
render() {
return (
<Switch>
<Route .../>
<Route .../>
</Switch>
);
}
}

Render component of nested routes ReactJS

I have nested routes that go 3 levels deep but I'm not able to render the component of my last route. This is how I structured my routes :
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/login" component={Login} />
<Route path="/" name="test" component={requireAuthentication(App)}>
<Route path="/modules" name="Modules" component={Modules}>
<Route path="virtual-inputs" name="Virtual Inputs" component={VirtualInput}>
<Route path="add" name="Add new virtual input" component={AddVirtualInput}/>
</Route>
</Route>
</Route>
</Router>
</Provider>
When I go to modules/virtual-inputs/add I get my component VirtualInput rendered.
I render inside modules my child components with this.props.children, but how can I render my component AddVirtualInput when going to /modules/virtual-inputs/add ?
I saw already on another thread (https://stackoverflow.com/a/33690586/846326) that another solution is to have my routes like this :
<Route path="/modules" name="Modules" component={Modules}>
<Route path="virtual-inputs" name="Virtual Inputs" component={VirtualInput}/>
<Route path="virtual-inputs/add" name="Add new virtual input" component={AddVirtualInput}/>
</Route>
Is this the way to do it, meaning we can't go further then 2 levels?
There is no restriction on the level of nesting of routes in React Router.
When you nest routes, the parent route component will receive the current matching child route component as this.props.children. As long as you don’t forget to use this.props.children, they should render just fine.
In your case, it should be enough to make sure that you use this.props.children in the render() method of the VirtualInput component. Otherwise it will receive AddVirtualInput in this.props.children but you won’t see it since it doesn’t get rendered.
If you already did that, please share the full code of the components to further diagnose the issue.

Categories