I cannot comprehend how to use React Router properly. I want a component, rendered at the root ("/") to have multiple routes and redirect to "/projects" by default. Routes that I want to have are these:
"/projects"
"/projects/project/:projectId/"
"/projects/project/:projectId/device/:deviceId/"
"/projects/project/:projectId/device/:deviceId/task/:taskId/"
Anyway, whatever I try, I cannot render anything past the second route despite link href matching the route path.
In a sense, I do not really want to have nested routes (at least not in the way they are implemented here, just replace components according to path instead of appending.
Here is CodeSandbox link with one of many attempts.
Here are my routes:
<Switch>
<Redirect exact from="/" to="/projects/" />
<Route path="/projects/" exact>
<p>List of projects.</p>
<Link to="/projects/project/1/">Project #1</Link>
</Route>
<Route path="/projects/project/:projectId/">
<p>List of devices</p>
<Link to="/projects/project/1/device/1/">Device #1</Link>
</Route>
<Route path="/projects/project/:projectId/device/:deviceId/">
<p>List of tasks</p>
<Link to="/projects/project/1/device/1/task/1/">Task #1</Link>
</Route>
<Route path="/projects/project/:projectId/device/:deviceId/task/:taskId/">
<p>Task info.</p>
</Route>
</Switch>
This can be solved by adding exact to the other routes.
Updated working Codesandbox
The reason for this is /projects/project/:projectId/ is the first matching route, even for a list of tasks because of the wildcard :projectId.
<Route exact path="/projects/project/:projectId/">
<p> List of devices </p>
<Link to="/projects/project/1/device/1/"> Device #1 </Link>
</Route>
<Route exact path="/projects/project/:projectId/device/:deviceId/">
<p> List of tasks </p>
<Link to="/projects/project/1/device/1/task/1/"> Task #1 </Link>
</Route>
<Route exact path="/projects/project/:projectId/device/:deviceId/task/:taskId/">
<p> Task info. </p>
</Route>
The issue is that in the Switch component that path order and specificity matters! Keep in mind that the Switch "Renders the first child <Route> or <Redirect> that matches the location."
To resolve you should reorder the routes in inverse order of path specificity. In other words, you should try to match the more specific paths before the less specific paths. When done correctly it removes the need to sprinkle the exact prop all over the code.
The exact prop is not necessary in about 99.9% of use cases.
Example:
<Switch>
<Route path="/projects/project/:projectId/device/:deviceId/task/:taskId">
<p>Task info</p>
</Route>
<Route path="/projects/project/:projectId/device/:deviceId">
<p>List of tasks</p>
<Link to="/projects/project/1/device/1/task/1">Task #1</Link>
... other task links ...
</Route>
<Route path="/projects/project/:projectId">
<p>List of devices</p>
<Link to="/projects/project/1/device/1">Device #1</Link>
... other device links ...
</Route>
<Route path="/projects">
<p>List of projects</p>
<Link to="/projects/project/1">Project #1</Link>
... other project links ...
</Route>
{/* Anything not handled above redirects to "/projects" */}
<Redirect to="/projects" />
</Switch>
Related
so I'm trying to create routing for my react.js app by using react-router v.6.
I already tried to do changes for the v.6 but for some reason my app didn't show anything, can anyone help me ?
So Navigation.js file is for Routing, Header.js filled with button for changing route and App.js is used to display them.
App.js:
<div>
<Header/>
<Outlet/>
</div>
Header.js:
<>
<Link to="/">Home</Link>
<Link to="/Productlist">Product</Link>
<Link to="/Contact">Contact Us</Link>
</>
Navigation.js:
<>
<Routes>
<Route exact path="/" element={<Home/>}>
<Route exact path="ProductList" element={<Product/>}/>
<Route exact path="ContactUs" element={<Contact/>}/>
</Route>
</Routes>
</>
I don't know if this will solve your problem but first you can start by replacing your Navigation.js by :
<Routes>
<Route path="/" element={<Home/>} />
<Route path="ProductList" element={<Product/>}/>
<Route path="ContactUs" element={<Contact/>}/>
</Routes>
In this case you don't need nested routes.
Then, I don't think it's a good idea to use capitalize letter in path.
I am designing a users dashboard in React, wherein User logs in and navigate to his dashboard with other links such as Archived, Profile and Settings. My Login and then Navigating to HomePage is working fine.
I have designed the Dashboard, Archived, Profile and Settings Page Links in Sidebar. Now when I am navigating to the Links. It takes me to a new URL path and my Sidebar disappears. I want my sidebar to still appear on all pages as long as I am logged in.
Below is my App.js where I have designed the upper level route:
return (
<div>
<Router history={history}>
<Switch>
<PrivateRoute exact path="/" component={HomePage} />
<Route path="/login" component={LoginPage} />
<Route path="/register" component={RegisterPage} />
<Redirect from="*" to="/" />
</Switch>
</Router>
</div>
);
Once User is on HomePage, I need the dashboard and other links to show. and when User clicks on any Sidebar link, Sidebar should still be there while the other page opens. So I added the inner Routes to the HomePage.jsx like this:
return (
<div>
<Router history={history}>
<Sidebar />
<Switch>
<Route path="/" component={Dashboard} />
<Route path="/archived" component={ArchivedPage} />
<Route path="/profile" component={ProfilePage} />
<Route path="/settings" component={SettingsPage} />
{/* <Redirect from="*" to="/" /> */}
</Switch>
</Router>
</div>
);
But it doesn't work. Can anyone please help me understanding if this is correct. or how can I achieve the required result.
Please let me know if you need any other details.
Thank you.
Issue
The issue is that you are exactly matching "/" to render HomePage, but then as soon as the path is any deeper, like "/archived", it no longer exactly matches and HomePage is unmounted.
You should not render a Router within another Router, you need only one router per app to provide the routing context.
Solution
Remove the inner router (and any other nested routers!!). Remove the exact prop on the "/" path and reorder the routes to specify the more specific paths before less specific paths so the Switch can actually do its job and match and render the appropriate route.
App
Reorder the more specific "/login" and "/register" paths to be matched prior to the less specific "/" path. If you had, for example, a "/login/google" path, this is more specific than "/login" and would be ordered earlier.
return (
<div>
<Router history={history}>
<Switch>
<Route path="/login" component={LoginPage} />
<Route path="/register" component={RegisterPage} />
<PrivateRoute path="/" component={HomePage} />
<Redirect to="/" />
</Switch>
</Router>
</div>
);
HomePage
Move the Dashboard to the last route position so if no other route above it is matched and returned then the dashboard is matched and rendered.
return (
<div>
<Sidebar />
<Switch>
<Route path="/archived" component={ArchivedPage} />
<Route path="/profile" component={ProfilePage} />
<Route path="/settings" component={SettingsPage} />
<Route component={Dashboard} />
</Switch>
</div>
);
in first place your doing something wrong you cant put a Router inside a Router,
you haver a Router in app then inside a component that is inside of app you have another Router thats a problem i dont know if that solves your problem but try it just delete Router in homepage
I have HTML structure like this:
<body>
<nav>
<!--navigation elements -->
</nav>
<div className='main'>
<!--other elements -->
</div>
<div className='container'></div>
</body>
And routing defined like this:
<Router>
<Fragment>
<Navbar />
<Route exact path="/" component={Landing} />
<div className="container">
<Alert />
<Switch>
<Route exact path="/register" component={Register} />
<Route exact path="/login" component={Login} />
<Route exact path="/profiles" component={Profiles} />
</Switch>
</div>
</Fragment>
</Router>
The "container" element is present on all routes however I do not want it to be rendered on the "/" route.
How can I stop <div className="container"> from being rendered on the "/" route? I want it to be rendered on all other routes except of "/".
A solution I found, but don't want to use is to explicitly insert the element with class="container" in each component that is rendered in my <Switch>. Is there a better way?
You should be able to achieve what you require via nested routes and a "no match route".
The idea would be to introduce structure to your routing via nested routes, to restrict rendering of <div className="container"> to non / routes.
To do this, you could extract a component (ie WithContainer) that renders a <Route> for paths; /register, /login and /profiles, inside of the <div className="container">. You would then change your <Switch> to now render two routes for the following route cases;
A <Route/> that renders the Landing component on an exact match of /
A <Route/> that renders your new WithContainer component on no specific route (ie any path that does not exactly match /)
By using the <Switch> in this way, it causes the routing behaviour to render either Landing or WithContainer (but not both) depending on the first matched route. We take advantage of that behaviour to restrict rendering of the WithContainer (and in turn, the <div className="container"> element) for "non /" routes.
In code, this approach could be expressed as:
const WithContainer = () => (
<div className="container">
{ /* Wrap routes in container div */ }
<Route exact path="/register" component={Register} />
<Route exact path="/login" component={Login} />
<Route exact path="/profiles" component={Profiles} />
</div>)
const Main = () => (
<main>
<Switch>
<Route exact path='/' component={Landing}/>
<Route component={ WithContainer } /> {/* No match route */ }
</Switch>
</main>
)
Hope that helps!
With the latest version of React Router, you can provide an array of strings for the path prop so that a specific route renders on multiple matches:
<Route path={['/one', '/two', '/three']} component={SomeComponent} />
Here's a link to the relevant documentation: https://reacttraining.com/react-router/web/api/Route/path-string-string.
If you don't want to create a separate component, you can just do this. You need to keep the inner switch as well, if you want to keep the original functionality.
// parent switch
<Switch>
<Route exact path="/" component={Landing} />
// start route wrap!!!
<Route>
<div className="container">
<Switch>
<Route exact path="/register" component={Register} />
<Route exact path="/login" component={Login} />
<Route exact path="/profiles" component={Profiles} />
</Switch>
</div>
</Route>
// end route wrap!!!
</Switch>
You should think of Routes as of any other UI components. You can nest them arbitrarily.
When I try to have nested routes on my root route I run into a problem.
I have 3 "main" routes:
<Switch>
<Route path="/" component={Home} />
<Route path="/login" component={Login} />
<Route path="/logout" component={Logout} />
</Switch>
On my Home component I have a nested router like this:
<div>
<Route path="/" render={() => <div>Home</div>} />
<Route path="/test" render={() => <div>Test Route</div>} />
</div>
The Home component has a sidebar HOC which contains the Links.
<Sidebar>
<Link to="/">Home</Link>
<Link to="/test">Test</Link>
<Link to="/logout">Logout</Link>
</Sidebar>
When Im on my Root component and click the Test link, the route on the nested router changes to the Test component which is correct. Whenever I go the login and/or logout route it tries to display that in the nested router in the Home component
Any idea what is going wrong?
EDIT: I've tried the example #Tholle provided. Unfortunately it still doesn't work the way I want to. See this CodeSandBox I made to reproduce my problem.
your links need to point to "/home/testX" and the nested routes need to handle "/home/testX". Also you will need a route for "/home" in the root. I don't believe the Link component is scoped to the route in which it is called. Meaning that to link to "/test1" assumes that is the base route. However, the rendering of test1 actually takes place in the home component.
To put it another way: In order to get to /home/test1 you must first get the home component (/home) to render which can then render the route for test1 (/home/test1)
Here's the codesandbox
Here is an example that provides a bit more flexibility codesandbox. This one will require a redirect of "/" because it depends on the path being used and it needs to be "/home" not "/".
Hope this helps. Hope I got it all right.
The Switch component makes sure that only the first Route that matches is rendered. To stop <Route path="/" component={Home} /> from always being rendered, you can set the exact prop to true.
Example (CodeSandbox)
<Switch>
<Route exact path="/" component={Home} />
<Route path="/login" component={Login} />
<Route path="/logout" component={Logout} />
</Switch>
I have been trying to understand how to apply the concept of nested routes to my scope.
I need these routes:
/projects
Title
List
NavLink to /projects/new
This route will have a list of projects and a button to create a new project.
/projects/new
Form
NavLink to close and go to /projects
When this route is called, will appear a panel above the content, it doesn't change the page projects.
/projects/:projectId/dashboard
Dashboard Title
List of items
When a project is selected, I need to show its dashboard in a whole new page, different than the /projects
My react route config:
<Switch>
<Route exact path='/home' component={Home}/>
<Route path='/home' component={Home}/>
<Route path='/projects' component={Projects} />
<Redirect to="/home" push />
</Switch>
My render inside projects to receive the other route:
<section className='content projects'>
<div>
<h3>List</h3>
<ul>{list}</ul>
</div>
<Route path='/projects/new' render={
() => {
return <div>
<h3>New Project</h3>
<form action="#">
<input type="text" placeholder='Contract name'/>
<input type="text" placeholder='Project name'/>
<button type="submit">Create</button>
</form>
<NavLink to='/projects'>Close</NavLink>
</div>
}
} />
</section>
Now, where should I put this route:
<Route path='/projects/:projectId/dashboard' component={Dashboard} />
I thought if I put inside the switch, would work, but the page gets blank.
Anyone can help me with this? =)
Thanks!
UPDATE 1
I tried to put my dashboard route above /projects with exact.
Now I'm getting a blank content where it should appear the Dashboard component. The route for projects is still working normally.
<Switch>
<Route exact path='/' component={Home}/>
<Route exact path='/projects/:projectId/dashboard' render={ () => {
return <h2>Dashboard</h2>
}} />
<Route path='/projects' component={Projects} />
<Redirect to="/home" push />
</Switch>
UPDATE 2
I have created a separated project and it worked with the answer in this topic. Now I will try to figure out why it didn't work in the project that I'm having blank page.
Try adding the exact prop to the Route and make sure that it appears BEFORE /projects in the Switch:
<Switch>
<Route exact path='/home' component={Home}/>
<Route path='/home' component={Home}/>
<Route exact path='/projects/:projectId/dashboard' component={Dashboard} />
<Route path='/projects' component={Projects} />
<Redirect to="/home" push />
</Switch>
What's happening is your first /projects is matching any route that starts with /projects. Adding the exact prop will make sure that if the route matches /projects/:projectId/dashboard exactly, it will render Dashboard.