I'm quite new in react. I'm trying to create a project where users would have to login to access a page and I want to protect one route that should be accessible only after succesful login. I don't want fancy user management or anything like that so please don't recommend context or redux. I just want my localhost:3000/editor to be accessible only after login and if someone tries to access /editor without login then they are redirected to login page. so something like isAuthenicated: true/false would be fine in my case I believe but I'm not sure how to pass that in my webapp. I would highly if someone can show me how to do that? I created this but many errors
App.js
import React, {useState} from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import "./App.css";
import Login from "./login";
import Dashboard from "./dashboard";
function App() {
const [loggedIN, setLoggedIN]=useState(false);
let privateRoutes = null;
if(loggedIN){
privateRoutes =(
<Route path="/dashboard" component={Dashboard} />
)}
return (
<>
<Router>
<div className="container">
<nav className="navbar navbar-expand-lg navheader">
<div className="collapse navbar-collapse">
<ul className="navbar-nav mr-auto">
<li className="nav-item">
<Link to={"/Login"} className="nav-link">
Login
</Link>
</li>
</ul>
</div>
</nav>
<br />
<Switch>
<Route exact path="/login" component={Login} />
{privateRoutes}
</Switch>
</div>
</Router>
</>);
}
export default App;
login.js
import React, { Component } from "react";
import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
import AppBar from "material-ui/AppBar";
import RaisedButton from "material-ui/RaisedButton";
import TextField from "material-ui/TextField";
import { Link } from "react-router-dom";
import "./loginForm.css";
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: ""
};
this.onchange = this.onchange.bind(this);
}
onchange(e) {
this.setState({ [e.target.name]: e.target.value });
}
performLogin = async () => {
var body = {
password: this.state.password,
email: this.state.email
};
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: JSON.stringify(body)
};
const url = "/api/authenticate";
try {
const response = await fetch(url, options);
const text = await response.text();
if (text === "redirect") {
this.props.setState({loggedIN: true})
this.props.history.push(`/dashboard`);
} else {
console.log("login failed");
window.alert("login failed");
}
} catch (error) {
console.error(error);
}
};
render() {
return (
<>
<div className="loginForm">
<MuiThemeProvider>
<TextField
hintText="Enter your Email"
floatingLabelText="Email"
onChange={(event, newValue) => this.setState({ email: newValue })}
/>
<br />
<TextField
type="password"
hintText="Enter your password"
floatingLabelText="password"
onChange={(event, newValue) =>
this.setState({ password: newValue })
}
/>
<br />
<RaisedButton
label="Submit"
primary={true}
style={style}
onClick={event => this.performLogin(event)}
/>
</MuiThemeProvider>
</div>
</>
);
}
}
const style = {
margin: 15
};
export default Login;
editor page
import React, { Component } from "react";
class Dashboard extends Component {
constructor(props) {
super(props);
}
logout=()=>{
this.setState({loggedIN: false)}
}
render() {
return (
<div>hello</div>;
<button onCLick={logout}>Logout</button>
)
}
}
export default Dashboard;
edit: codesandbox
After successfully logged in, maybe you will get token for authorization purpose. so after successfully logged in you can store the auth token in cookies.
install - npm i universal-cookie --save
login.js
import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
import AppBar from "material-ui/AppBar";
import RaisedButton from "material-ui/RaisedButton";
import TextField from "material-ui/TextField";
import { Link } from "react-router-dom";
import "./loginForm.css";
import Cookies from 'universal-cookie';
const cookies = new Cookies();
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: ""
};
this.onchange = this.onchange.bind(this);
}
onchange(e) {
this.setState({ [e.target.name]: e.target.value });
}
performLogin = async () => {
var body = {
password: this.state.password,
email: this.state.email
};
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: JSON.stringify(body)
};
const url = "/api/authenticate";
try {
const response = await fetch(url, options);
const text = await response.text();
// here you will get auth token in response
// set token in cookie like cookie.set('token', response.token);
cookies.set('loggedin', true);
if (text === "redirect") {
this.props.setState({loggedIN: true})
this.props.history.push(`/dashboard`);
} else {
console.log("login failed");
window.alert("login failed");
}
} catch (error) {
console.error(error);
}
};
render() {
return (
<>
<div className="loginForm">
<MuiThemeProvider>
<TextField
hintText="Enter your Email"
floatingLabelText="Email"
onChange={(event, newValue) => this.setState({ email: newValue })}
/>
<br />
<TextField
type="password"
hintText="Enter your password"
floatingLabelText="password"
onChange={(event, newValue) =>
this.setState({ password: newValue })
}
/>
<br />
<RaisedButton
label="Submit"
primary={true}
style={style}
onClick={event => this.performLogin(event)}
/>
</MuiThemeProvider>
</div>
</>
);
}
}
const style = {
margin: 15
};
export default Login;
After that in the dashboard component check for the loggedin boolean in a cookie if it exists then its logged in and authenticated. eg.
dashboard.js
import React, { Component } from "react";
import {Redirect} from 'react-router-dom'
import Cookies from 'universal-cookie';
const cookies = new Cookies();
class Dashboard extends Component {
constructor(props) {
super(props);
}
logout=()=>{
this.setState({loggedIN: false)}
cookies.set('loggedin', false);
}
render() {
// notice here
if (!cookies.get('loggedin')) {
return (<Redirect to={'/login'}/>)
}
return (
<div>hello</div>;
<button onCLick={logout}>Logout</button>
)
}
}
export default Dashboard;
I think you have error here or it is better to change it
let privateRoutes = null;
if(loggedIN){
privateRoutes =(
<Route path="/dashboard" component={Dashboard} />
)}
change it to
let privateRoutes =()=>{
if(loggedIN){
return(
<Route path="/dashboard" component={Dashboard} />
)
}
}
but you don't need this! you could use render route
<Route path="/home" render={() => {
if(loggedIN){
return(<Dashboard ...props/>)
}else{
return(<Login ...props/>)
}
}} />
note that after login ((loggedIN)) will be true in App.js and it will go to Dashboard automatically
ref: https://reacttraining.com/react-router/web/api/Route/render-func
here you have Big Bugs:
import React, { Component } from "react";
class Dashboard extends Component {
constructor(props) {
super(props);
}
// logout function should not be here it should write in app.js and pass to
// this component with props and just call here if needed
// and you should use this.logout = () ...
logout=()=>{
// and here
this.setState({loggedIN: false**)**}
}
render() {
// here!!! jsx element should wrapped with an element
return (
// <div>
<div>hello</div>;
<button onCLick={logout}>Logout</button>
//</div>
)
}
}
export default Dashboard;
The most efficient way is to split routes and manage redirect with an HOC like [redux-auth-wrapper][2]
App.js
import { createDevTools } from 'redux-devtools'
import LogMonitor from 'redux-devtools-log-monitor'
import DockMonitor from 'redux-devtools-dock-monitor'
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
import { Provider } from 'react-redux'
import thunkMiddleware from 'redux-thunk'
import * as reducers from './reducers'
import App from './components/App'
const reducer = combineReducers(Object.assign({}, reducers, {}))
const DevTools = createDevTools(
<DockMonitor toggleVisibilityKey="ctrl-h"
changePositionKey="ctrl-q">
<LogMonitor theme="tomorrow" />
</DockMonitor>
)
const enhancer = compose(
// Middleware you want to use in development:
applyMiddleware(thunkMiddleware),
DevTools.instrument()
)
// Note: passing enhancer as the last argument requires redux@>=3.1.0
const store = createStore(reducer, enhancer)
ReactDOM.render(
<Provider store={store}>
<div>
<App />
{/* <DevTools /> */}
</div>
</Provider>,
document.getElementById('mount')
)
auth.js
import locationHelperBuilder from 'redux-auth-wrapper/history4/locationHelper'
import { connectedRouterRedirect } from 'redux-auth-wrapper/history4/redirect'
import connectedAuthWrapper from 'redux-auth-wrapper/connectedAuthWrapper'
import Loading from './components/Loading'
const locationHelper = locationHelperBuilder({})
const userIsAuthenticatedDefaults = {
authenticatedSelector: state => state.user.data !== null,
authenticatingSelector: state => state.user.isLoading,
wrapperDisplayName: 'UserIsAuthenticated'
}
export const userIsAuthenticated = connectedAuthWrapper(userIsAuthenticatedDefaults)
export const userIsAuthenticatedRedir = connectedRouterRedirect({
...userIsAuthenticatedDefaults,
AuthenticatingComponent: Loading,
redirectPath: '/login'
})
export const userIsAdminRedir = connectedRouterRedirect({
redirectPath: '/',
allowRedirectBack: false,
authenticatedSelector: state => state.user.data !== null && state.user.data.isAdmin,
predicate: user => user.isAdmin,
wrapperDisplayName: 'UserIsAdmin'
})
const userIsNotAuthenticatedDefaults = {
// Want to redirect the user when they are done loading and authenticated
authenticatedSelector: state => state.user.data === null && state.user.isLoading === false,
wrapperDisplayName: 'UserIsNotAuthenticated'
}
export const userIsNotAuthenticated = connectedAuthWrapper(userIsNotAuthenticatedDefaults)
export const userIsNotAuthenticatedRedir = connectedRouterRedirect({
...userIsNotAuthenticatedDefaults,
redirectPath: (state, ownProps) => locationHelper.getRedirectQueryParam(ownProps) || '/protected',
allowRedirectBack: false
})
Working example based on React Router v4
What I do
in main routing (usualli App.js):
import { userIsAuthenticated, userIsNotAuthenticated } from './auth';
// ...code
const isNotAuth = userIsNotAuthenticated(Login);
const isAuth = userIsAuthenticated(AuthRoutes);
// ...code
<Switch>
<Route exact path="/login" component={isNotAuth} /> // Notice the *exact* path
<Route path="/" component={isAuth} /> // Notice the *not exact path*
<Route path="" component={NotFoundPage} />
</Switch>
AuthRoutes.js
Those routes will be provided only if the user is authenticated
// ...code
<Switch>
<Route exact path="/homepage" component={HomePage} />
// ...other routes
</Switch>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.