problem with router and privaterouter / history - reactjs

Hello I have a problem redirecting to a page doing a verification on a privaterouter
Unhandled Rejection (TypeError): Cannot read property 'push' of
undefined
on this line:
this.props.history.push ("/ home");
my component:
import React, { Component } from 'react';
import api from '../services/api';
import { withRouter } from 'react-router';
class LoginForm extends Component {
constructor(props){
super(props);
this.state = {
login:'',
password:'',
};
this.onSubmit = this.onSubmit.bind(this);
this.onChange = this.onChange.bind(this);
}
async onSubmit(e){
e.preventDefault();
const {login, password } = this.state;
const response = await api.post('/login', { login,password });
const user = response.data.user.login;
const {jwt} = response.data;
localStorage.setItem('token', jwt);
localStorage.setItem('user', user);
this.props.history.push("/home");
}
onChange(e){
this.setState({[e.target.name]: e.target.value});
}
render() {
const { errors, login, password, isLoading } = this.state;
return (
<form onSubmit={this.onSubmit}>
<label htmlFor="login">Login</label>
<input type="text" name="login" id="login" value={login} onChange={(e) => this.onChange(e)} placeholder="Informe seu login" />
<label htmlFor="password">Senha</label>
<input type="password" name="password" id="password" value={password} onChange={(e) => this.onChange(e)} placeholder="Informe sua senha"/>
<button className="btnEnt" type="submit">Entrar</button>
</form>
)
}
}
export default withRouter (LoginForm);
my router:
import React from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import Login from './pages/login/index';
import DashBoard from './pages/dashboard/index';
import PrivateRoute from './auth';
export default function Routes(){
return(
<BrowserRouter>
<div>
<Switch>
<Route path="/" exact component = {Login}/>
<PrivateRoute path="/home" component = {DashBoard}/>
</Switch>
</div>
</BrowserRouter>
);
}
my private route or auth router:
import React from 'react';
import { Route, Redirect} from 'react-router-dom';
const isAuth = () => {
console.log('a');
if(localStorage.getItem('token') !== null) {
console.log('true')
return true;
}
return false;
};
const PrivateRoute = ({component: Component, ...rest}) => {
return (
<Route
{...rest}
render={props =>
isAuth() ? (
<Component {...props} />
): (
<Redirect
to={{
pathname: '/',
state: {message: 'Usuário não autorizado'}
}}
/>
)}
/>
);
}
export default PrivateRoute;
I basically have my router and I also check if the user is allowed to enter this page, but I'm having trouble making it work.

Well, I read your code and here is my answer
You just need import withRouter from react-router-dom and not from react-router ;)
import { withRouter } from "react-router-dom";
And use it like
export default withRouter(LoginForm);

Related

'this.props.displaychange is not a function' when I use Route

*Sorry in advance I couldn't split the code into separate components in the post
I have a question that I will be able to explain well
I have an authentication component that receives a prop from another component
Now when I put all the components under one big parent component that contains all the components everything works fine and the props pass successfully
But as soon as I start using route I get this message
"Uncaught (in promise) TypeError: this.props.displaychange is not a function"
whet I tried to do here is to change the state in the TeamList component (state.displayTeams to true)
import './App.css';
import React from 'react'
import { Route, Routes } from 'react-router-dom'
import Home from './components/Home/Home';
import LogIn from './components/Log-in/Log-in';
import SignIN from './components/Sign-in/Sign-in';
import TeamList from './components/Team-list/Team-list';
import TeamDisplay from './components/TeamDisplay/TeamDisplay';
function App() {
return (
<div>
<Routes>
<Route path='/' exact element={<Home />} />
<Route path='/login' element={<LogIn />} />
<Route path='/sign-in' element={<SignIN />} />
<Route path='/teamlist' element={<TeamList />} />
<Route path='/teamdisplay' element={<TeamDisplay />} />
</Routes>
</div>
);
}
export default App;
import React, { Component } from "react";
import { signInWithEmailAndPassword, signOut } from 'firebase/auth'
import { auth } from '../../firebase-config'
import './Log-in.css'
import { Link } from "react-router-dom";
class LogIn extends Component {
state = {
logEmail: '',
logPass: '',
user: '',
login: false
}
login = async (displaychange, e) => {
e.preventDefault()
this.setState({ login: true })
const user = await signInWithEmailAndPassword(auth, this.state.logEmail, this.state.logPass)
console.log(user.user.email)
this.setState({ user: user.user.email })
this.setState({ logEmail: '', logPass: '' })
console.log('logged in')
this.props.displaychange()
}
logOut = async (displaychangeOut, e) => {
e.preventDefault()
if (this.state.login) {
await signOut(auth)
this.setState({ user: '', logEmail: '', logPass: '' })
console.log('you out')
this.props.displaychangeOut()
} else {
return
}
}
render() {
return (
<div className="Login-form">
<form>
<span>ACCOUNT LOGIN</span>
<label>USERNAME </label>
<input value={this.state.logEmail} onChange={(e) => this.setState({ logEmail: e.target.value })} name="nameLog" placeholder="Your name..." />
<label>PASSWORD </label>
<input value={this.state.logPass} onChange={(e) => this.setState({ logPass: e.target.value })} name="passLog" placeholder="Your password..." />
<button onClick={(e) => this.login(this.props.displaychange, e)}>LOG IN</button>
</form>
</div>
)
}
}
export default LogIn
import React, { Component } from "react";
import { db } from '../../firebase-config'
import { collection, addDoc, getDocs, getDoc, doc } from 'firebase/firestore'
import TeamDisplay from "../TeamDisplay/TeamDisplay";
import LogIn from "../Log-in/Log-in";
const teamCollectionRef = collection(db, 'Teams')
class TeamList extends Component {
state = {
teams: [
],
displayTeams: false
}
componentDidMount() {
getDocs(teamCollectionRef)
.then(snap => {
let teams = []
snap.docs.forEach(doc => {
teams.push({ ...doc.data() })
});
this.setState({ teams: teams });
});
}
changedisplay = () => {
this.setState({ displayTeams: true })
}
changedisplayOut = () => {
this.setState({ displayTeams: false })
}
render() {
let displayTeam
if (this.state.displayTeams) {
displayTeam = this.state.teams.map(item => <TeamDisplay key={item.teamId} team={item} />)
} else {
displayTeam = null
}
return (
<div>
{displayTeam}
</div>
)
}
}
export default TeamList

React history.push not working from the Home component

I'm implementing authentication to my React project and I'm trying to have the Login form in my Home component. This is how my Home component looks with the Login component attached to it.
import Login from './Login'
import * as React from 'react';
import Box from "#material-ui/core/Box";
const Home = () => {
return (
<div>
<Box sx={{
width: 600,
height: 150,
backgroundColor: 'lightgrey',
borderRadius: 16
}}>
{<h1>Welcome to our Podcast App</h1>}
{<h2>Feel free to share your favorites or have fun listening to the ones available.</h2>}
</Box><br/>
< Login />
</div>
);
};
export default Home;
The problem is, I want to redirect my users to the podcasts page and, history.push is not working. This is how the Login component looks.
import React from "react";
import { connect } from "react-redux";
import { loginUser } from "../actions/index";
import Button from "#material-ui/core/Button";
import { Box } from "#material-ui/core";
class Login extends React.Component {
state = {
email: "",
password: "",
error: false
};
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value
});
};
handleSubmit = (event) => {
event.preventDefault();
const { email, password } = this.state;
this.props
.dispatchLoginUser({ email, password })
.then(() => this.props.history.push('/podcasts'))
.catch(() => this.setState({ error: true }));
};
render() {
return (
<Box sx={{ p: 2, border: '1px solid grey' }}>
<form onSubmit={this.handleSubmit} >
<h3>Log In</h3>
<p>{this.state.error && "Invalid email or password"}</p>
<fieldset>
<label htmlFor='email'>
Email:
</label>
<input
type='text'
name='email'
id='email'
onChange={this.handleChange}
value={this.state.email}
/>
</fieldset>
<fieldset>
<label htmlFor='password'>
Password:
</label>
<input
type='password'
name='password'
id='password'
onChange={this.handleChange}
value={this.state.password}
/>
</fieldset><br/>
<Button variant="contained" size="small" color="inherit" type='submit'>Log In</Button>
</form>
</Box>
);
}
}
const mapDispatchToProps = (dispatch) => {
return {
dispatchLoginUser: (credentials) => dispatch(loginUser(credentials))
};
};
export default connect(null, mapDispatchToProps)(Login);
If I try to log in from the Login page, it will work. But it doesn't work from the Home page. I also tried adding history to my project like another post suggested but it still doesn't work. Here's the code:
//history.js
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
//index.js
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
ReactDOM.render(
<Provider store={ store }>
<Router history={history} >
<App />
</Router>
</Provider>
//The routes in App.js
<Route exact path='/' component={Home}/>
<Route exact path='/signup' component={Signup} />
<Route exact path='/login' component={Login} />
Is it possible to make history.push work from the Home component? Any suggestion will be welcomed. Thank you all.
In react-router-dom version 6
useHistory() is replaced by useNavigate()
import {useNavigate} from 'react-router-dom';
const navigate = useNavigate();
navigate('/home')
Is it possible to make history.push work from the Home component?
Yes, absolutely it is. You just need to destructure it from the props object in the Home component. For this the Home component needs to actually have a defined/declared props object.
const Home = (props) => {
// access props.history
return (
...
<Login history={props.history} />
...
);
};
or
const Home = ({ history }) => {
// access history
return (
...
<Login history={history} />
...
);
};
Home is a function component, so it can also use the useHistory hook and pass it along.
const Home = () => {
const history = useHistory();
return (
...
<Login history={history} />
...
);
};

Undefined function in React Router + Redux

Currently writing application in React + Redux stack. During development, I hit the problem where in one's of React component, Redux function work just fine and in index.js throwing that Redux function is undefined.
What I wish to do, is to keep user sign in between reloading of web page. So where pages reload, componentDidMonut function in index.js check if there is a token in localstorage then send one api call to check if JWT token did not expired. And if everthing is correct call redux function signIn() to rerender app.
In Login component login working and redirect to after sign in, unfortunately in index.js React throwing error:
.
After checking in console this function is simply undefined, and I don't know why.
Looking forward for pointing mistakes I made. Thanks in advance
Below code:
actions/index.js:
export const signIn = () => {
return {
type: "SIGN_IN"
};
};
export const signOut = () => {
return {
type: "SIGN_OUT"
};
};
reducers/authReducers:
const INITIAL_STATE = {
isSignedIn: null
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case "SIGN_IN":
return { ...state, isSignedIn: true };
case "SIGN_OUT":
return { ...state, isSignedIn: false };
default:
return state;
}
};
Login.js:
import React from "react";
import {
Button,
Container,
Form,
Grid,
Header,
Message,
Segment
} from "semantic-ui-react";
import { connect } from "react-redux";
import { signIn, signOut } from "../actions";
import api from "../api/api";
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
isSignedIn: null,
email: "",
password: "",
errorMessage: ""
};
this.errorMessageRef = React.createRef();
}
handleLogin = async () => {
try {
const response = await api.login(this.state.email, this.state.password);
if (response.status === 200) {
localStorage.setItem("token", response.data.token);
this.props.signIn();
this.props.history.push("/flat");
}
} catch (error) {
this.errorMessageRef.current.hidden = false;
this.setState({ ...this.state, errorMessage: `${error}` });
this.props.signOut();
}
};
render() {
return (
<Container>
<Grid centered columns={2}>
<Grid.Column>
<Header as="h2" textAlign="center">
Login
</Header>
<Segment>
<Form size="large">
<Form.Input
fluid
icon="user"
iconPosition="left"
placeholder="Email address"
onChange={event =>
this.setState({ email: event.target.value })
}
/>
<Form.Input
fluid
icon="lock"
iconPosition="left"
placeholder="Password"
type="password"
onChange={event =>
this.setState({ password: event.target.value })
}
/>
<Button
color="blue"
fluid
size="large"
onClick={this.handleLogin}
>
Login
</Button>
</Form>
</Segment>
<Message ref={this.errorMessageRef} negative={true} hidden={true}>
Email or password and incorrect
</Message>
<Message>
Not registered yet? Sign Up
</Message>
</Grid.Column>
</Grid>
</Container>
);
}
}
export default connect(null, { signIn, signOut })(Login);
index.js:
import React from "react";
import { createStore } from "redux";
import ReactDOM from "react-dom";
import { connect, Provider } from "react-redux";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import { Container } from "semantic-ui-react";
import reducers from "./reducers";
import { signIn, signOut } from "./actions";
import api from "./api/api";
import Login from "./components/Login";
import App from "./components/pages/App.js";
import Flat from "./components/pages/Flat";
import NavBar from "./components/NavBar";
import Duties from "./components/pages/Duties";
import MyDuties from "./components/pages/MyDuties";
import CreateFlat from "./components/flat/CreateFlat";
import JoinFlat from "./components/flat/JoinFlat";
import Payments from "./components/pages/Payments";
import "./components/index.css";
const store = createStore(reducers);
class Routing extends React.Component {
constructor(props) {
super(props);
this.state = { isSignedIn: null };
}
componentDidMount() {
this.isLoggedIn();
}
isLoggedIn = async () => {
try {
if (localStorage.getItem("token") != null) throw "user not sign in";
await api.getCurrentFlatmate();
this.props.signIn();
} catch (e) {
this.props.signOut();
}
};
render() {
return (
<Router>
<NavBar />
<Switch>
<Route exact path="/" component={App} />
<Route path="/home" component={App} />
<Route path="/sign-in" component={Login} />
<Route path="/sign-out" component={App} />
<Route path="/user" component={Login} />
<Route path="/flat" component={Flat} />
<Route path="/join-flat" component={JoinFlat} />
<Route path="/create-flat" component={CreateFlat} />
<Route path="/duties" component={Duties} />
<Route path="/my-duties" component={MyDuties} />
<Route path="/payments" component={Payments} />
</Switch>
</Router>
);
}
}
export default connect(null, { signIn, signOut })(Routing);
ReactDOM.render(
<Provider store={store}>
<Routing />
</Provider>,
document.getElementById("root")
);
Try using this.props.dispatch(signOut()) instead of this.props.signOut().
And in the same way for signIn()

Page doesn't load on redirect reactjs + redux

I am trying to redirect a user after login to home page, the redirect happens (this I know because the url changes and I go to a new page), but the content of the page doesn't load. I keep on getting this error
Warning: You tried to redirect to the same route you're currently on: "/index"
Here's my code for Login.js:
import React, { Component } from 'react';
import '../App.css';
import 'whatwg-fetch';
import { connect } from 'react-redux';
import { loginUser } from './../store/actions/loginActions';
import { Redirect, withRouter } from 'react-router-dom';
class Login extends Component {
constructor(props) {
super(props);
this.state = {
Email: '', Password: '', isloggedin: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ [event.target.name]: event.target.value });
}
handleSubmit(event) {
event.preventDefault();
this.props.loginUser(this.state)
}
render() {
const { authError, token } = this.props;
if(token) return <Redirect to="/index" /> // I'm telling it to redirect if the state has a token
return (
<div className="Background">
<Card
title="Login"
style={{ width: 400 }}
>
<Form onSubmit={this.handleSubmit} className="login-form">
<Form.Item>
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} name="Email" type="text" onChange={this.handleChange} />
</Form.Item>
<Form.Item>
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" name="Password" onChange={this.handleChange} />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" disable='notloggedin' className="login-form-button">
Log in
</Button>
</Form.Item>
</Form>
<div>
{ authError ? <p> { authError } </p> : null }
</div>
</Card>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
authError: state.auth.authError,
token: state.auth.token
}
}
const mapDispatchToProps = (dispatch) => {
return {
loginUser: (data) => dispatch(loginUser(data))
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps) (Login));
My action is:
export const loginUser = (data) => {
return (dispatch, getState) => {
fetch(url, {
credentials: 'include',
method: 'POST',
headers: headers,
body: JSON.stringify(data)})
.then(res => { if (res.ok) {
return res.json() }
else {
return Promise.reject({
status: res.status,
statusText: res.statusText
})
}
})
.then(function(response) {
dispatch({type: 'LOGIN_USER', value: response.AccessToken });
})
.catch((err) => {
dispatch({type: 'LOGIN_USER_FAILED', err });
})
}
};
and my App.js is
import React, { Component } from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import './App.css';
import Login from './components/Login';
import Home from './components/Home';
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<h3 className="App-logo"> App Load </h3>
</header>
<BrowserRouter>
<Switch>
<Route path="/" component={Login} />
<Route path="/index" component={Home} />
</Switch>
</BrowserRouter>
);}}
export default App;
Could anyone please let me know if they see any reason as to why the redirect is working but not loading the page components? I'll really appreciate any pointers.
try to wrap you whole App component into BrowserRouter
I shifted this <Route path="/" component={Login} /> to the bottom of my routes in App.js and it worked.

React App Login Error : Maximum update depth exceeded

I am using react with redux to build login authentication system with backend passport jwt .
login works fine before , i have added PrivateRoute to some Routes that requires authentication.
Errors i got :
src/actions/authActions.js
import { GET_ERRORS,CLEAR_ERRORS,SET_CURRENT_USER,LOGOUT_USER} from './types';
import axios from 'axios';
import setAuthToken from '../utils/setAuthToken';
import jwt_decode from 'jwt-decode';
export const loginUser= userdata =>dispatch=>{
axios.post('/api/auth/login',userdata)
.then(res=>{
console.log('loginUser action response ==>',res.data);
const {token}=res.data;
localStorage.setItem('jwtToken',token);
setAuthToken(token);
// Decode token to get user data
const decoded = jwt_decode(token);
dispatch(setCurrentUser(decoded));
}).catch(err=>{
dispatch({type:GET_ERRORS,payload:err.response.data});
})
}
// Set logged in user
export const setCurrentUser = decoded => {
return {
type: SET_CURRENT_USER,
payload: decoded
};
};
src/reducers/authReducers.js
import isEmpty from '../validation/is-empty';
import { SET_CURRENT_USER,LOGIN_USER,LOGOUT_USER} from '../actions/types';
const initialState = {
isAuthenticated: false,
user: {}
};
export default function(state = initialState, action) {
switch (action.type) {
case LOGIN_USER:
case SET_CURRENT_USER:
return {
...state,
isAuthenticated: !isEmpty(action.payload),
user: action.payload
};
case LOGOUT_USER:
return {
...state,
isAuthenticated:false,
user: {}
};
default:
return state;
}
}
App.js
import React, { Component } from 'react';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import {Provider} from 'react-redux';
import store from './store';
import Footer from './partials/footer';
import Header from './partials/header';
import Login from './components/auth/login';
import { setCurrentUser ,logoutUser} from './actions/authActions';
import jwt_decode from 'jwt-decode';
import setAuthToken from './utils/setAuthToken';
import PrivateRoute from './utils/PrivateRoute';
import Dashboard from './components/user/dashboard';
import NotFound404 from './components/error/404';
if(localStorage.jwtToken){
setAuthToken(localStorage.jwtToken);
// Decode token and get user info and exp
const decoded = jwt_decode(localStorage.jwtToken);
store.dispatch(setCurrentUser(decoded));
// Check for expired token
const currentTime = Date.now() / 1000;
if (decoded.exp < currentTime) {
// Logout user
store.dispatch(logoutUser());
// Clear current Profile
//store.dispatch(clearCurrentProfile());
// Redirect to login
window.location.href = '/login';
}
}
export default class App extends Component {
constructor(){
super();
this.state={
isAuthenticated:store.getState().auth.isAuthenticated
}
}
render() {
return (
<Provider store={store}>
<Router>
<div className="App">
<Header/>
<div className="container">
<Switch>
<Route exact path="/" component={Home}/>
<Route exact path="/login" component={Login} />
<PrivateRoute isAuthenticated={this.state.isAuthenticated} exact path="/dashboard" component={Dashboard}/>
<Route component={NotFound404} />
</Switch>
</div>
<Footer/>
</div>
</Router>
</Provider>
);
}
}
src/components/login.js
import React, { Component } from 'react'
import { Link } from 'react-router-dom';
import classnames from 'classnames';
import { connect } from 'react-redux';
import { loginUser } from '../../actions/authActions';
import { PropTypes } from 'prop-types';
class Login extends Component {
constructor(){
super();
this.state={
email:'',
password:'',
errors:{}
}
this.handleChange=this.handleChange.bind(this);
this.handleSubmit=this.handleSubmit.bind(this);
}
handleChange(event){
this.setState({
[event.target.name]:event.target.value
});
}
handleSubmit(event){
event.preventDefault();
const user={
email:this.state.email,
password:this.state.password
}
this.props.loginUser(user);
}
componentDidMount() {
if (this.props.auth.isAuthenticated) {
this.props.history.push('/dashboard');
}
}
componentWillReceiveProps(nextProps){
if(nextProps.errors){
this.setState({
errors:nextProps.errors
});
}
if(nextProps.auth.isAuthenticated){
this.props.history.push('/dashboard');
}
}
render () {
const {errors} = this.state;
return (
<div className="row my-5">
<div className="col-md-4 offset-md-4 col-sm-12">
<div className="card shadow-sm">
<h5 className="card-header">Login</h5>
<div className="card-body">
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="email" className="label">Email</label>
<input type="email" id="email" name="email" value={this.state.email} onChange={this.handleChange} className={classnames('form-control',{'is-invalid':errors.email})}/>
{errors.email && (<div className="invalid-feedback">{errors.email}</div>)}
</div>
<div className="form-group">
<label htmlFor="password" className="label">Password</label>
<input type="password" id="password" name="password" value={this.state.password} onChange={this.handleChange} className={classnames('form-control',{'is-invalid':errors.password})}/>
{errors.password && (<div className="invalid-feedback">{errors.password}</div>)}
</div>
<button type="submit" className="btn btn-success btn-block">Login</button>
</form>
<div className="py-3 border-bottom"></div>
<Link to="/register" className="btn btn-default btn-block my-2">Haven't created account yet ?</Link>
<Link to="/forgotpassword" className="btn btn-default btn-block">Forgot Password ?</Link>
</div>
</div>
</div>
</div>
)
}
}
const mapStateToProps = (state, ownProps) => ({
auth:state.auth,
errors:state.errors
})
const mapDispatchToProps = {
loginUser
}
Login.propTypes={
auth:PropTypes.object.isRequired,
errors:PropTypes.object.isRequired,
loginUser:PropTypes.func.isRequired
}
export default connect(mapStateToProps,mapDispatchToProps)(Login)
PrivateRoute.js component
import React from 'react';
import {Route,Redirect} from 'react-router-dom';
const PrivateRoute=({component: Component, isAuthenticated, ...rest}) => {
return (
<Route
{...rest}
render={(props) => isAuthenticated === true
? <Component {...props} />
: <Redirect to={{pathname: '/login', state: {from: props.location}}} />}
/>
)
}
export default PrivateRoute;
Please help me to solve this error .
I have solved my error by replacing PrivateRoute component as below :
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);
I suggest you use another state variable for saving request status. like loggingIn if it's true, show loading if it's false and isAuthenticated is false, the user is not requested for login and it's not logged in. so redirect it to /login.
PrivateRoute.js component
import React from 'react';
import {Route,Redirect} from 'react-router-dom';
class PrivateRoute extends Component {
render() {
const {
component: Component, loggingIn, isAuthenticated, ...rest
} = this.props;
if (loggingIn) {
return (
<div>
Please wait.
</div>
);
}
return (<Route {...rest} render={props => (isAuthenticated ? (<Component {...props} />) : (<Redirect to={{ pathname: '/login', state: { from: props.location } }} />))} />);
}}
export default PrivateRoute;
src/actions/authActions.js
import { GET_ERRORS,CLEAR_ERRORS,SET_CURRENT_USER,LOGOUT_USER} from './types';
import axios from 'axios';
import setAuthToken from '../utils/setAuthToken';
import jwt_decode from 'jwt-decode';
export const loginUser= userdata =>dispatch=>{
dispatch(loggingIn(true));
axios.post('/api/auth/login',userdata)
.then(res=>{
dispatch(loggingIn(false));
console.log('loginUser action response ==>',res.data);
const {token}=res.data;
localStorage.setItem('jwtToken',token);
setAuthToken(token);
// Decode token to get user data
const decoded = jwt_decode(token);
dispatch(setCurrentUser(decoded));
}).catch(err=>{
dispatch(loggingIn(false));
dispatch({type:GET_ERRORS,payload:err.response.data});
})
}
// Set logged in user
export const setCurrentUser = decoded => {
return {
type: SET_CURRENT_USER,
payload: decoded
};
};
export const loggingIn = status => {
return {
type: 'LOGGINGIN',
status,
}
}
src/reducers/authReducers.js
import isEmpty from '../validation/is-empty';
import { SET_CURRENT_USER,LOGIN_USER,LOGOUT_USER} from '../actions/types';
const initialState = {
isAuthenticated: false,
user: {}
};
export default function(state = initialState, action) {
switch (action.type) {
case LOGIN_USER:
case SET_CURRENT_USER:
return {
...state,
isAuthenticated: !isEmpty(action.payload),
user: action.payload
};
case LOGOUT_USER:
return {
...state,
isAuthenticated:false,
user: {}
};
case 'LOGGINGIN':
return {
...state,
loggingIn: action.status,
};
default:
return state;
}
}
and remember pass loggingIn as props to privateRoute
Edit : show how to use in App.js
App.js
import React, { Component } from 'react';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import {connect} from 'react-redux';
import Footer from './partials/footer';
import Header from './partials/header';
import Login from './components/auth/login';
import { setCurrentUser ,logoutUser} from './actions/authActions';
import jwt_decode from 'jwt-decode';
import setAuthToken from './utils/setAuthToken';
import PrivateRoute from './utils/PrivateRoute';
import Dashboard from './components/user/dashboard';
import NotFound404 from './components/error/404';
class App extends Component {
constructor(props){
super(props);
const { dispatch } = props;
if(localStorage.jwtToken){
setAuthToken(localStorage.jwtToken);
// Decode token and get user info and exp
const decoded = jwt_decode(localStorage.jwtToken);
dispatch(setCurrentUser(decoded));
// Check for expired token
const currentTime = Date.now() / 1000;
if (decoded.exp < currentTime) {
// Logout user
dispatch(logoutUser());
// Clear current Profile
//dispatch(clearCurrentProfile());
// Redirect to login
window.location.href = '/login';
}
}
}
render() {
const { isAuthenticated, loggingIn } = this.props;
return (
<Provider store={store}>
<Router>
<div className="App">
<Header/>
<div className="container">
<Switch>
<Route exact path="/" component={Home}/>
<Route exact path="/login" component={Login} />
<PrivateRoute loggingIn={loggingIn} isAuthenticated={isAuthenticated} exact path="/dashboard" component={Dashboard}/>
<Route component={NotFound404} />
</Switch>
</div>
<Footer/>
</div>
</Router>
</Provider>
);
}
}
const mapStateToProps = state = {
const { loggingIn, isAuthenticated } = state.auth;
return { loggingIn, isAuthenticated }
}
export default connect(mapStateToProps)(App);

Resources