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登录错误:超过最大更新深度的主要内容,如果未能解决你的问题,请参考以下文章

ReactJs抛出最大更新深度错误

超过最大更新深度 - React Js

ReactJs 超过最大更新深度

反应“错误:超过最大更新深度。”带有功能组件和钩子

React Typescript with hooks:最大更新深度超出错误

阿波罗钩子超过了最大更新深度