I am trying to make a Private Route that when the user logs in will refresh and redirect to the same componenent after logging in via /authentication route or else if he is already logged in then go to the component directly
import React, { useContext, useEffect } from "react";
import { Route, Redirect } from "react-router-dom";
import AuthContext from "../auth/AuthContext";
import Layout from "../layout/Layout"
const PrivateRoute = ({ component: Component, ...rest }) => {
const authContext = useContext(AuthContext);
const { isAuthenticated } = authContext;
return (
<Route
{...rest}
render={props =>(
<Layout>
{!isAuthenticated ? (
<Redirect to={`/authentication?redirect=${props.location.pathname}${props.location.search}`} />
) : (
<Component {...props} />
)}
</Layout>
)}
/>
);
};
export default PrivateRoute;
/authentication is the route that takes to the login page.
Currently this program work fine until it takes to the route correctly .
For eg: http://localhost:3000/authentication/?redirect=/live
but when the user logs in via this ...it do not redirect to /live and just stays there.
What is wrong?
Related
I am working on a basic react auth app, right now the routes /signup and /login work when I run this repo with my .env.local file that contains firebase auth variables.
https://github.com/MartinBarker/react-auth-app
I am trying to make it so that the '/' route that points to Dashboard will only be accessible for a user who is currently signed in, and if a user is not signed in but tries to access the '/' route they will be redirected to the '/login' page.
But whenever I use the route
<PrivateRoute exact path="/" element={Dashboard} />
my chrome devtools console shows a blank page with error messages:
index.tsx:24 Uncaught Error: [PrivateRoute] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>
my PrivateRoute.js looks like this:
// This is used to determine if a user is authenticated and
// if they are allowed to visit the page they navigated to.
// If they are: they proceed to the page
// If not: they are redirected to the login page.
import React from 'react'
import { Navigate, Route } from 'react-router-dom'
import { useAuth } from '../Contexts/AuthContext'
const PrivateRoute = ({ component: Component, ...rest }) => {
// Add your own authentication on the below line.
//const isLoggedIn = AuthService.isLoggedIn()
const { currentUser } = useAuth()
console.log('PrivateRoute currentUser = ', currentUser)
return (
<Route
{...rest}
render={props =>
currentUser ? (
<Component {...props} />
) : (
//redirect to /login if user is not signed in
<Navigate to={{ pathname: '/login'}} />
)
}
/>
)
}
export default PrivateRoute
Im not sure why this error is occurring, any help is appreciated
This behaviour seems to have changed in ReactRouter V6 here is the solution we came up with for a project.
Private route
*Re-creating the users question code
import React from 'react'
import { Navigate, Route } from 'react-router-dom'
import { useAuth } from '../Contexts/AuthContext'
const PrivateRoute = ({ children }) => {
// Add your own authentication on the below line.
//const isLoggedIn = AuthService.isLoggedIn()
const { currentUser } = useAuth()
console.log('PrivateRoute currentUser = ', currentUser)
return (
<>
{
currentUser ? (
children
) : (
//redirect to /login if user is not signed in
<Navigate to={{ pathname: '/login'}} />
)
}
</>
)
}
export default PrivateRoute
Typescript
*Our actual code implementation of this issue
const PrivateRoute: React.FC = ({ children }) => {
const navigate = useNavigate();
const { isAuthenticated, isAuthLoading } = useAuth();
const { user, isLoadingUser } = useContext(UserContext);
// Handle users which are not authenticated
// For example redirect users to different page
// Show loader if token is still being retrieved
if (isAuthLoading || isLoadingUser) {
// TODO: show full page loader
return (
<div>Loading...</div>
);
}
// Proceed with render if user is authenticated
return (
<>
{children}
</>
);
};
Router
<Router>
<Routes>
<Route
path={routes.user.accountSignup.path}
element={
<PrivateRoute>
<AccountSignup />
</PrivateRoute>
}
/>
</Routes>
</Router>
Upon entering my credentials, I successfully can redirect to the <Gallery/> component. However, once on the <Gallery/> component, if I want to switch from /gallery to /, I shouldn't be able to unless I logout (that of which I haven't implemented, yet) but unfortunately - I'm able to and I'm not sure why.
My question is: How's this happening and how can I remain on /gallery even if I manually go to my browser's search bar and switch my path to / which's the <Login/> component? I think something might be off with my <ProtectedRoute/> but I'm unsure.
Here's App.js:
import ReactDOM from 'react-dom';
import LoginSignUp from "./LoginSignUp/LoginSignUp";
import Gallery from "./Gallery/Gallery";
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import ProtectedRoute from "./ProtectedRoute/ProtectedRoute";
function App() {
return (
<>
<Switch>
<Route exact path="/" component={LoginSignUp} />
<ProtectedRoute component={Gallery} path='/gallery'/>
</Switch>
</>
);
}
if (document.getElementById("main")) {
ReactDOM.render(<Router><App/></Router>, document.getElementById("main"));
}
Here's ProtectedRoute.js:
import React from "react";
import {Redirect, Route, useHistory} from "react-router-dom";
function ProtectedRoute({ component: Component, ...restOfProps }) {
const isAuthenticated = localStorage.getItem("token");
console.log("this", isAuthenticated);
return (
<Route
{...restOfProps}
render={(props) =>
isAuthenticated ? <Component {...props} /> : <Redirect to='/' />
}
/>
);
}
export default ProtectedRoute;
I have used this code as the private route, but I want a another separate route only for admins. How can I change the code?
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import {connect } from 'react-redux'
import PropTypes from 'prop-types'
const Private = ({ component: Component,
auth:{loading, isAuthenticted},
...rest
}) => (
<Route
{...rest}
render={props =>
!isAuthenticted && loading ?
(<Redirect to='/signin'/>
):(
<Component {...props}/>)
}
/> );
Private.propTypes={
auth:PropTypes.object.isRequired,
}
const mapStateToProps=state=>({
auth:state.auth
})
export default connect(mapStateToProps)(Private);
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import {connect } from 'react-redux'
import PropTypes from 'prop-types'
import Spinner from '../layouts/Spinner';
const Admin = ({ component: Component,
auth:{loading, isAuthenticated,user},
...rest
}) => (
<Route
{...rest}
render={props =>
loading ? (
<Spinner />
) : isAuthenticated ? (
(user.userRole=='Admin')?
<Component {...props} />:( <Redirect to="/index" />)
) : (
<Redirect to="/signin" />
)
}
/> );
Admin.propTypes={
auth:PropTypes.object.isRequired,
}
const mapStateToProps=state=>({
auth:state.auth
})
export default connect(mapStateToProps)(Admin);
I added user Role field which is already in my state.
I have a login system with React-Redux and I want to display JSX when a specific dispatch Type is send.
For example when the login system failed the dispatch type is LOGIN_FAIL and I want to display an error message but when the authentication is successful then the dispatch type is LOGIN_SUCCESS and I want to display a success message. I already access the username trough mapStateToProps but I was curious if there is another way trough the dispatch type to do conditional rendering?
Thanks for your advice or tips on that topic.
This is my actions.js:
export const login = (email, password) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify({ email, password });
try {
const res = await axios.post(`${process.env.REACT_APP_API_URL}/auth/jwt/create/`, body, config);
dispatch({
type: LOGIN_SUCCESS,
payload: res.data
});
dispatch(load_user());
} catch (err) {
dispatch({
type: LOGIN_FAIL
});
}
};
When i do user authentication, and i want to conditially render a component. I use React routing dom. This way i can create a PrivateRoute that will render the individual component out and redirect if the user !isAuthenticated. Check this out /
https://blog.bitsrc.io/build-a-login-auth-app-with-mern-stack-part-1-c405048e3669
This is the tutorial that you can clone the git
Private Route:
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import PropTypes from "prop-types";
const PrivateRoute = ({ component: Component, auth, ...rest }) => (
<Route
{...rest}
render={props =>
auth.isAuthenticated === true ? (
<Component {...props} />
) : (
<Redirect to="/login" />
)
}
/>
);
PrivateRoute.propTypes = {
auth: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth
});
export default connect(mapStateToProps)(PrivateRoute);
MAIN APP:
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import jwt_decode from "jwt-decode";
import setAuthToken from "./utils/setAuthToken";
import { setCurrentUser, logoutUser } from "./actions/authActions";
import { Provider } from "react-redux";
import store from "./store";
import Navbar from "./components/layout/Navbar";
import Landing from "./components/layout/Landing";
import Register from "./components/auth/Register";
import Login from "./components/auth/Login";
import PrivateRoute from "./components/private-route/PrivateRoute";
import Dashboard from "./components/dashboard/Dashboard";
import "./App.css";
// Check for token to keep user logged in
if (localStorage.jwtToken) {
// Set auth token header auth
const token = localStorage.jwtToken;
setAuthToken(token);
// Decode token and get user info and exp
const decoded = jwt_decode(token);
// Set user and isAuthenticated
store.dispatch(setCurrentUser(decoded));
// Check for expired token
const currentTime = Date.now() / 1000; // to get in milliseconds
if (decoded.exp < currentTime) {
// Logout user
store.dispatch(logoutUser());
// Redirect to login
window.location.href = "./login";
}
}
class App extends Component {
render() {
return (
<Provider store={store}>
<Router>
<div className="App">
<Navbar />
<Route exact path="/" component={Landing} />
<Route exact path="/register" component={Register} />
<Route exact path="/login" component={Login} />
<Switch>
<PrivateRoute exact path="/dashboard" component={Dashboard} />
</Switch>
</div>
</Router>
</Provider>
);
}
}
export default App;
I am new to react and developing an application which allows user to navigate across pages.
I created a TypeScript Project with following
App.tsx
import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import Login from './login';
import Welcome from "./welcome";
import About from './about';
const RedirectRoute = (props) => {
// if (!authenticated) {
// }
return <Route {...props} />
}
const App: React.FC = () => {
return (
<BrowserRouter>
<div>
<Switch>
<RedirectRoute path="/" component={Welcome} exact/>
<RedirectRoute path="/login" component={Login} exact/>
<RedirectRoute path="/about" component={About} exact/>
</Switch>
</div>
</BrowserRouter>
);
};
export default App;
welcome.tsx
import React from 'react';
import {Redirect} from 'react-router-dom';
import {Button} from 'antd';
import Login from './login';
import About from './about';
const Welcome: React.FC = () => {
const redir = (page: string) => {
console.log('Page:', page);
if (page === 'login'){
// <Redirect to="/login"/>
} else if(page === 'about') {
//<Redirect to="/about"/>
}
}
return (
<div style={{textAlign: 'center'}}>
<div style={{display:'inline-block'}}>
<Button
key="login"
type="primary"
onClick={() => redir('login')}
>Login Page</Button>
<Button
key="about"
type="primary"
style={{marginTop:'20px'}}
onClick={() => redir('about')}
>Navigation Bar</Button>
</div>
</div>
);
}
export default Welcome;
about.tsx
import React from "react";
const About: React.FC = () => {
return <div>About Page</div>;
};
export default About;
login
import React from 'react';
const Login: React.FC = () => {
return (
<div>Login Page</div>
);
}
export default Login;
Based on routing configurations in App.tsx pages loading fine (localhost:3000/about or localhost:3000/login and localhost:3000/)
But the problem occurs when I tries to redirect to a page when clicking a button(In my real scenario, I have to navigate to welcome page once login is successful)..
Please clarify on following questions if possible since I am confused:
1. Routing works fine when landing in a page, how to manage it when I want to redirect to a page via code and url should reflect.
2. What is the best way to keep these kinda routing and will it support history when user clicks on back button of browser?
Thank you.
You probably need history
import { createHashHistory } from 'history'
export const history = createHashHistory({
hashType: 'hashbang'
})
Later importing in inside file:
import { history } from '../helpers'
Put this line of code inside function responsible for authorization / authentication - after subbmiting - this line push user to the path is needed.
history.push('/login')
To navigate via a router on click, you should pass properties to component used in . To do that, replace
const Welcome: React.FC = () => {
with
const Welcome: React.FC = (props) => {
and in redir method instead
if (page === 'login'){
// <Redirect to="/login"/>
} else if(page === 'about') {
//<Redirect to="/about"/>
}
use
if (page === 'login'){
this.props.history.push("/login");
} else if(page === 'about') {
this.props.history.push("/about");
}