I'm trying to handle unmatched routes in my app by having them redirect to the login screen, I've set my routes up like so:
<Switch>
{
session.IsLoggedIn === false &&
<Container fluid className="d-flex flex-column h-100">
<Route exact path="/" component={ Login } />
<Route path="/ResetPassword/:token" component={ ResetPassword } />
<Redirect to="/" />
</Container>
}
</Switch>
With this setup though, even if I enter a legitimate route, i.e /ResetPassword/3245924, it still redirects to the login screen.
Note: I've gotten the above setup by following this tutorial: https://www.surajsharma.net/blog/react-router-default-route
How would I ensure that the legitimate routes are hit while redirecting on non handled routes?
You doesnt have wrapped your redirect in default router, therefore it will be always rendered/triggered
for correct behaviour wrap it in "default" route
<Switch>
{
session.IsLoggedIn === false &&
<Container fluid className="d-flex flex-column h-100">
<Route exact path="/" component={ Login } />
<Route path="/ResetPassword/:token" component={ ResetPassword } />
<Route path="*"><Redirect to="/" /></Route>
</Container>
}
</Switch>
I figured the issue out, it seems to be because the <Container> element was housing the routes, not the <Switch> element. When I moved the <Switch> element into the <Container>, making the <Route> elements direct descendants of the element it worked.
Issue
The Switch component exclusively renders the first matching Route or Redirect component. When it hits anything other than one of these two components, i.e. the Container, it stops matching and just renders that component/element. Once not rendered directly by a Switch, all Route and Redirect components are inclusively matched and rendered. The Redirect component doesn't have a from prop so it will always be matched and effect a navigation change.
Solution
Since the Switch isn't rendering any Route or Redirect components it appears it's unnecessary where it is currently. Move it into the conditional rendering to directly wrap the routing components.
{!session.IsLoggedIn &&
<Container fluid className="d-flex flex-column h-100">
<Switch>
<Route path="/ResetPassword/:token" component={ResetPassword} />
<Route path="/" component={Login} />
<Redirect to="/" />
</Switch>
</Container>
}
Technically speaking, the path "/" is already a path-prefix for every other route path, so the Redirect isn't actually reachable. You should be able to remove it since path="/" will catch and match anything that wasn't already matched prior to it.
{!session.IsLoggedIn &&
<Container fluid className="d-flex flex-column h-100">
<Switch>
<Route path="/ResetPassword/:token" component={ResetPassword} />
<Route path="/" component={Login} />
</Switch>
</Container>
}
For best practice, all default paths should lead to your landing page...Routing during runtime is supposed to be managed by React backend. You could look into service workers for your own proxy. I suggest you remove the: session.IsLoggedIn === false && and just use a regular route initialization ...and use useState or onComponentDidMount to automatically load the content you want- you can make that page a component. oh my apologies for the confusion, I'm glad you got this resolved.
Related
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 my app like that
<Router>
<div className="App">
<Link to="/" style={{ textDecoration: "none" }}>
<header>Project Issues</header>
</Link>
<Switch>
<Route exact path="/">
<Issues />
</Route>
</Switch>
</div>
</Router>
Inside Issues component, I choose to render one of two components based on a window width condition, one of the two components is called TwoPagesLayout, in this component when the user clicks on some text, it should go to another component called IssueDetails.
Here's the part of the TwoPagesLayout component:
{issues.map((issue: any) => (
<div>
<Link to="/Details" style={{ textDecoration: "none" }}>
<p className="issue-title">{issue.title}</p>
</Link>
<hr></hr>
<Switch>
<Route exact path="/Details">
<IssueDetails issue={issue} />
</Route>
</Switch>
</div>
))}
The problem is when I click on the text, it goes to that url "http://localhost:3000/Details" but it appears as a blank white page, it doesn't render what is inside the page.
Hope I have made it clear, I am new to react so I think the question maybe sounds common.
<Route exact path="/">
<Issues />
</Route>
Since you've marked this as exact, once the url changes to /Details, it no longer matches, and so Issues unmounts. Since Issues unmounts, so too does its descendant IssueDetails. You probably want to do:
<Route path="/">
<Issues />
</Route>
The Issues component will only render on the "/" path. When the page is showing "/Details" the Issues component will not render, and that includes the nested Router.
For this reason (they’re confusing), I personally try to avoid nested routers, so I would suggest keeping all the Route components together unless you have a reason otherwise!
Recently I've tried building a web platform trough React. Everything's fine, everything's work ecc.
But I've run in many problems when I tried to create a different page for the user login:
I already had Routes in my code, so when I tried to add other Routes to another js file they simply didn't work.
I have no clue how to do the authentication in react router, I've tried in many ways, followed many tutorials but nothing worked out.
<Switch>
<Route exact path='/' component={Feed}/>
<Route path="/user" component={Profilo}/>
<Route path='/explore-page' component={ExplorePage} />
<Route path='/events-page' component={EventsPage} />
<Route path='/calendar-page' component={CalendarPage} />
<Route path='/stats-page' component={StatsPage} />
<Route path='/form' component={FormPage}/>
<Route path="/user" component={Profilo}/>
<Route path="/user/:userId" component={Profilo} />
</Switch>
This is all the routes I'm currently using inside a div to get the react component rendered.
As I said before adding other routes in an upper file wouldn't give me a response.
So, in the end, I'm gonna ask you where should I put the route for the login and the home? In there or I should just moving everything?
Thank you in advance.
One simple way is adding the logic to handle authentication in your render function.
If the user is not authenticated. Redirect to the login page. Otherwise, go to your component
render() {
if (!this.props.isAuth) {
return (
<Switch>
<Route exact path="/" component={Login} />
<Redirect to="/" />
</Switch>
);
}
return (<Switch>
<Route
// your router
/>
</Switch>);
}
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 am creating an application using ReactJS. I am using react router v4 from react-router-dom.
I have written routes in index.js file.
<BrowserRouter>
<Switch>
<Route exact path="/" component={Login} />
<Route exact path='/dashboard' component={Viewport} />
</Switch>
</BrowserRouter>
Rest of the application in Viewport.js file.
return (
<div className="">
<Sidebar navigation={this.viewport} />
<HeaderBanner user={this.props.user} />
<div className="center-panel">
//todo
//Can I use router here?
</div>
</div>
)
After user login's, I am rendering Viewport which contains Sidebar and header bar by default. Based on the item click in the sidebar navigation, I need to render components dynamically. As of now, if I write anything in the place of todo, it renders only that component for the complete browser window.
Is there any way to use routers in multiple places of the application? If yes, how can I implement it? If no, what's the best solution?
As far as I have seen, routers will be stacked at one place in the application.
Thanks in advance.
I followed a tutorial on youtube recently which was very useful
So I took some of it and applied it to your setup
<BrowserRouter>
<div>
<Route exact path="/" component={Login} />
<Route exact path='/dashboard' component={Viewport} />
</div>
</BrowserRouter>
import { NavLink, Route } from 'react-router-dom';
class Viewport extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<div className="Side-bar">
<NavLink
activeClassName="active"
to={`${this.props.match.url}/sub-page-name-1`}>Sub Page 1</NavLink>
<NavLink
activeClassName="active"
to={`${this.props.match.url}/sub-page-name-2`}>Sub Page 2</NavLink>
<NavLink
activeClassName="active"
to={`${this.props.match.url}/sub-page-name-3`}>Sub Page 3</NavLink>
</div>
<HeaderBanner user={this.props.user} />
<div className="center-panel">
<Route path={`${this.props.match.url}/sub-page-name-1`} component={SubPagePanel1} />
<Route path={`${this.props.match.url}/sub-page-name-2`} component={SubPagePanel2} />
<Route path={`${this.props.match.url}/sub-page-name-3`} component={SubPagePanel3} />
</div>
</div>
)
}
}
I removed Switch as well because I didn't use it for my sub pages... :-S
Update: Have created a repo showing a working example of sub page content
https://github.com/PocketNinjaDesign/so-sub-routes-answer
Yes you can use <Routes> in as many places as you want. <Router> components are the ones you can only use once.