React App登录错误:超过最大更新深度
Posted
技术标签:
【中文标题】React App登录错误:超过最大更新深度【英文标题】:React App Login Error : Maximum update depth exceeded 【发布时间】:2019-03-10 20:37:06 【问题描述】:我正在使用 react with redux 来构建带有后端护照 jwt 的登录身份验证系统。
之前登录工作正常,我已将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=>
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 组件
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;
请帮我解决这个错误。
【问题讨论】:
我不确定但是,Login
组件应该添加到您的问题中 Redirect
组件所指的位置,如果您认为没有必要,请不要担心。
@ibubi ,添加有问题的登录组件
【参考方案1】:
我建议您使用另一个状态变量来保存请求状态。比如loggingIn
如果为真,如果为假则显示加载,isAuthenticated
为假,不要求用户登录,也未登录。所以将其重定向到/login
。
PrivateRoute.js 组件
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;
记住将 loggingIn
作为 props 传递给 privateRoute
编辑:展示如何在 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);
【讨论】:
【参考方案2】:我已通过替换PrivateRoute
组件解决了我的错误,如下所示:
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);
【讨论】:
以上是关于React App登录错误:超过最大更新深度的主要内容,如果未能解决你的问题,请参考以下文章