React Router: Excluding a route from root path [duplicate] - reactjs

This question already has answers here:
How to create a protected route with react-router-dom?
(5 answers)
Closed 5 months ago.
I'm currently setting up basic authentication for my React app. I have an AppWithAuth class which wraps the normal App, and a Login page. My goal is to redirect to /login when a user is not authenticated, but allow them to visit any other route if they are, such as / (dashboard path), /users, etc.
The problem I have is that the app wishes to render the root directory, but will redirect to the Login if not Authenticated. But since the login route is included in the root directory render, the app endlessly redirects. Any way to achieve the desired effect? Here's the gist of my code:
App:
class App extends React.Component {
render() {
return (
<Router> {/* The main routes of the app, e.g. /users... */}
...
</Router>
)
}
}
AppWithAuth:
class AppWithAuth extends React.Component {
isAuthenticated = () => {
// suppose this returns true if user is authenticated, false otherwise
}
render() {
return (
<Router>
<Route path='login' component={Login} />
<Route path='/' render={props => {
return this.isAuthenticated ? (
<App />
) : (
<Redirect to='/login' />
)
}} />
</Router>
)
}
}

If you use Switch then you can match paths exclusively. In that case order matters - the first path to match will break the switch, just like the switch statement. You can also leverage exact attribute to match a path exactly if you need to. For example:
<Router>
<Switch>
<Route exact path="/login" component={Login} />
<Route
path="/"
render={props => {
return this.isAuthenticated ? (
<App />
) : (
<Redirect to="/login" />
);
}}
/>
<Route path="/home" component={App} />
</Switch>
</Router>

what about try to this?
make ProtectedRoute for authenticated users
export default ({ isAuthenticated, component: C, ...rest }) => {
if (isAuthenticated) {
<Route {...rest} render={props => <C {...props} />} />
} else {
return <Redirect to="/login" />
}
}
use Router, Route in App.js
import ProtectedRoute from './ProtectedRoute'
class App extends React.Component {
isAuthenticated = () => {
// suppose this returns true if user is authenticated, false otherwise
}
render() {
return (
<Router>
<Route path='login' component={Login} />
<ProtectedRoute path='/' component={Main} isAuthenticated={this.isAuthenticated()} />
</Router>
)
}
}
The ProtectedRoute is only accept for authenticated users any others redirect to /login

Related

Redirect and show the component if user is logged in in React

I have a login page which is a public route and a dashboard page which is a private route as follows:
App.js:
function App() {
return (
<Router>
<div className="App">
<Switch>
<PublicRoute path="/" exact component={Login} />
<PrivateRoute path="/dashboard" component={Dashboard} />
</Switch>
</div>
</Router>
);
}
And this is my private route component:
let store = require("store");
const PrivateRoute = ({component: Component, ...options}) => {
// const finalComponent = user != null && user.auth == true ? component : Login;
const [userData, setUserData] = useState({});
useEffect(() => {
setUserData(store.get("userData"));
}, []);
return (
<Route
{...options}
render={(props) =>
store.get("userData") ?
<Component {...props} />
:
<Redirect to="/"/>
}
/>
);
};
export default PrivateRoute;
Now the problem that I am facing is that if I open the dashboard localhost:9999/dashboard none of my components are visible because they are routed inside the dashboard component.
So when I open this localhost:9999/dashboard I want react to redirect to localhost:9999/dashboard/home and also show the components if the user is logged in.
I tried to do this :
return (
<Route
{...options}
render={(props) =>
store.get("userData") ?
<Redirect to="/dashboard/home" component={<Component {...props}} />
:
<Redirect to="/"/>
}
/>
);
This did route to the home page but nothing was visible at all. I mean the components inside dashboard weren't visible at all as well.
This is what I am getting. You see it redirects but doesn't show anything:
I believe that you could be more specific about which file is the code that you are showing.
But I feel that what you are trying to accomplish something like this:
PrivateRouter
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
const PrivateRoute = ({component: Component, restricted, ...rest}) => {
return (
<Route {...rest} render={props => (
user.auth == true ?
<Redirect to="/dashboard" />
: <Redirect to="/dashboard/home" />
)} />
);
};
export default PrivateRoute;

React router is not redirecting based on condition

I am creating a school management system using ReactJS. In this I want to redirect the login page to dashboard when isLoggedIn props changes from false to true. From the console I make sure it is changing but I can't perform a conditional routing based on this props. Props is changing but not redirecting.
import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom'
...
class App extends Component {
renderRoutes = () => {
const { isLoggedIn } = this.props.auth
return isLoggedIn ?
<Redirect to='dashboard' /> :
<Redirect to='/' />
}
render() {
return (
<BrowserRouter>
<Navbar />
<Switch>
<Route exact path="/" component={Authenticate} />
<Route path="/dashboard" component={DashboardTeachers} />
{this.renderRoutes()}
</Switch>
</BrowserRouter>
)
}
}
I want to redirect to '/dashboard' after successful login and redirect to '/' after successful logout
You can wrap private routes inside separate component and redirect from there.
Create a PrivateRoute component first:
function PrivateRoute({ auth, children, ...rest }) {
const { isLoggedIn } = auth;
return (
<Route
{...rest}
render={({ location }) =>
isLoggedIn ? (
children
) : (
<Redirect
to="/login"
/>
)
}
/>
);
}
Replace your code
<Route path="/dashboard" component={DashboardTeachers} />
{this.renderRoutes()}
with this
<PrivateRoute auth={auth}>
<Route path="/dashboard" component={DashboardTeachers} />
</PrivateRoute>
And in your login component, redirect after successful authentication
if (!isLoggedIn) {
return (
// your login ui
);
}
// redirect to dashboard if login is false
return <Redirect to="/dashboard" />;

How to show 404 Page when user is typing any URL on login page if user is not authenticated?

I want user to be blocked by accessing invalid URL from login screen if user is not authenticated, for instance, consider user is on login screen and if user tries to access any random url localhost:3000/kanskd, he/she should be redirected to login screen. I am able to achieve what i need by placing NoMatch route component, however, it matches the route inside my application as well and it renders No match for those routes as well[Routes that i am mapping after NoMatch route does not work].
index.js
import Routes from './routes'
<Switch>
<Route exact path="/" render={() => {
if(!store.getState().login.isAvailable) {
return <Redirect to="/login"/>
} else {
return <Redirect to="/dashboard" />
}
}}
/>
<Route exact path="/login" component={Login} />
<Route component={NoMatch} />
{Routes.map((prop, key) => {
return <Route path={prop.path} key={key} component={prop.component}
/>;
})}
</Switch>
NoMatch.jsx
import React from 'react'
import { withRouter } from 'react-router-dom';
const NoMatch = ({ location }) => (
<div>
<h3>No match for <code>{location.pathname}</code></h3>
</div>
)
export default withRouter(NoMatch);
EDIT:
routes/index.js
import Dashboard from "Dashboard/Dashboard.jsx";
var Routes = [{ path: "/", name: "Dashboard", component: Dashboard }];
export default Routes;
Once the user logs in, it routes him to Dashboard and in Dashboard, there are other multiple routes.
So you have to solve 2 things here: show the NoMatch component when there is no match for a url and protect some routes from not logged users.
For the first one you should put your <Route component={NoMatch} /> just before the <Switch>closing tag, think of this like a switch in plain javascript, the last case is always the default case, if there is no other match the default will be executed, same as here.
The second problem requires a bit of extra code, you have to create a component that redirects the user if is not logged in, something like this (this is from the documentation react-router docs):
function PrivateRoute({ component: Component, isLoggedIn,...rest }) {
return (
<Route
{...rest}
render={props =>
isLoggedIn ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
}
Then use this component for protected routes:
<Switch>
<Route exact path="/" render={() => {
if(!store.getState().login.isAvailable) {
return <Redirect to="/login"/>
} else {
return <Redirect to="/dashboard" />
}
}}
/>
<Route exact path="/login" component={Login} />
{Routes.map((prop, key) => {
return <PrivateRoute path={prop.path} key={key} component={prop.component} isLoggedIn={isUserLoggedIn}
/>;
})}
<Route component={NoMatch} />
</Switch>
isUserLoggedIn is a made up variable, you should replace it for you logged in checks methods
Edit:
The path should be /dashboard:
import Dashboard from "Dashboard/Dashboard.jsx";
var Routes = [{ path: "/dashboard", name: "Dashboard", component: Dashboard }];
export default Routes;
if you want to maintain / as your path you should return the dashboard component inside your Route component instead of redirecting:
<Route exact path="/" render={() => {
if(!store.getState().login.isAvailable) {
return <Redirect to="/login"/>
} else {
return <Dashboard/>
}
}}
/>

Layout routes with react router

I'm trying to do layouts with react-router.
When my user hits / I want to render some layout. When my user hits /login, or /sign_up I want the layout to render, with the relevant component for /login or /sign_up rendered.
Currently, my App.js looks like this
return (
<div className={className}>
<Route path="/" component={Auth} />
<ModalContainer />
</div>
);
My Auth.js looks like this
return (
<AuthFrame footerText={footerText} footerClick={footerClick}>
<Route path="/login" component={LoginContainer} />
<Route path="/sign_up" component={SignUpContainer} />
</AuthFrame>
);
So AuthFrame will get rendered when I hit /, and then react router looks for login or sign_up to render the other containers.
However, when I hit /, only the AuthFrame will render.
I would like for / to be treated as /login.
How do I achieve this?
The Switch component is useful in these cases:
return (
<AuthFrame footerText={footerText} footerClick={footerClick}>
<Switch>
<Route path="/login" component={LoginContainer} />
<Route path="/sign_up" component={SignUpContainer} />
{/* Default route in case none within `Switch` were matched so far */}
<Route component={LoginContainer} />
</Switch>
</AuthFrame>
);
see: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Switch.md
I think you're forced to introduce a prop/state which indicates the status of your viewer. This means is he signed in or just a guest of your website.
Your router can't obviously render /login if you you hit / but the router allows you to redirect to another page:
class AuthContainer extends React.Component {
defaultProps = {
loggedIn: false
}
render() {
return <div>
<Route path="/login" component={LoginContainer}/>
<Route path="/sign_up" component={SignUpContainer}/>
</div>
}
}
class PublicHomePage extends React.Component {
render() {
return <div>
<Route path="/settings" component={SettingsComponent}/>
<Route path="/profile" component={ProfileComponent}/>
<Route path="/and_so_on" component={AndSoOnComponent}/>
</div>
}
}
class App
extends React.Component {
defaultProps = {
loggedIn: false
}
render() {
const {loggedIn} = this.props;
if (loggedIn) {
return <PublicHomePage/>
}
return <Route exact path="/" render={() => (
<Redirect to="/login"/>
)}/>
}
}
I hope this code works for you. It isn't quite perfect but it should give you an idea how you could solve your problem.
In your case I would probably manipulate a bit with Routes in react-router. This code in AuthFrame should do the trick:
return (
<AuthFrame footerText={footerText} footerClick={footerClick}>
{["/", "/login"].map((path, ind) =>
<Route exact key={ind} path={path} component={LoginContainer} />
)}
<Route exact path="/sign_up" component={SignUpContainer} />
</AuthFrame>);
Note the usage of exact on the routes, this is to prevent matching login component on /sign_up since it will also match / and prevent rendering both login and signup when accessing the root path (/).

React Router Authentication Redirection

I am currently implementing an authentication/login flow with React and React Router V4. But I am struggling to find a working redirect "schema" to implement my idea.
The situation is as follows:
If a user is not logged in to my system he/she should get redirect
to "/login", and a specific login page component is rendered. This should be a requirement of the system, so that I can share register and login links for my application.
If the user is logged in he/should should be redirected to the route the user originally wants to visit.
My current implementation:
Entry Component
export default class Entry extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Router>
<Routes />
</Router>
);
}
}
Routes Component (authentication checking takes place here)
class Routes extends PureComponent {
componentDidMount() {
this.props.loadUser(); // async method (redux) loads the actual logged in user
}
render() {
return (
<Switch>
<Route path="/login" render={() => (!this.props.user.username ? <LoginPage {...this.props}/> : <Redirect to="/app" />)} />
<Route path="/app" render={() => (this.props.user.username ? <AppPage {...this.props} /> : <Redirect to="/login" />)} />
<Route exact path="/" render={props => <Redirect to="/app" />} />
</Switch>
);
}
}
export default Routes;
App component (nested routing here)
export default class App extends React.Component {
constructor(props) {
super(props);
}
render() {
const { match, user, logout } = this.props;
return (
<Switch>
<Route path={`${match.path}/about`} component={About} />
<Route path={`${match.path}`} component={Home} />
</Switch>
);
}
}
The Problem now occurs when the following is happening:
The user is logged in but has closed the tab of my application
The user now wants to visit /app/about
The routes component is loading but this.props.user.username is null
User gets redirected to /login
Now the async method this.props.loadUser() has updated the redux store and this.props.user.username is not null anymore and then the user gets redirected to /app but he originally wanted to visit /app/about.
So the line which makes me headaches is
<Route path="/login" render={() => (!this.props.user.username ? <LoginPage {...this.props}/> : <Redirect to="/app" />)} />
How should I handle this specific approach so that the user gets redirected to the URL he/she originally wanted to visit ?
Maybe my overall approach is a little bit weird.
Thanks in advance, I appreciate every help :)
Since you're using react-router v4 I recommend going to their great docs about this topic. Here
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
fakeAuth.isAuthenticated ? (
<Component {...props}/>
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location }
}}/>
)
)}/>
)
const AuthExample = () => (
<Router>
<div>
<AuthButton/>
<ul>
<li><Link to="/public">Public Page</Link></li>
<li><Link to="/protected">Protected Page</Link></li>
</ul>
<Route path="/public" component={Public}/>
<Route path="/login" component={Login}/>
<PrivateRoute path="/protected" component={Protected}/>
</div>
</Router>
);

Resources