Ошибка входа в приложение React: превышена максимальная глубина обновления - PullRequest
0 голосов
/ 05 октября 2018

Я использую реагирование с избыточностью для создания системы аутентификации входа с внутренним паспортом jwt.

До этого вход в систему работал нормально, я добавил PrivateRoute к некоторым маршрутам, требующим аутентификации.

Ошибки, которые я получил:

enter image description here enter image description here

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 / redurs / 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;

Пожалуйста, помогите мнерешить эту ошибку.

Ответы [ 2 ]

0 голосов
/ 08 октября 2018

Я решил свою ошибку, заменив компонент 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);
0 голосов
/ 05 октября 2018

Я предлагаю вам использовать другую переменную состояния для сохранения статуса запроса.например, 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 / redurs / 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 как реквизиты для 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...