在历史记录推送后,react路由器不会重新渲染

时间:2019-06-25 05:40:29

标签: reactjs react-redux

在用户登录后,我希望重新渲染/刷新,所以我使用history.push来完成。

import {history} from '../layout/Navbar'
export const loginUser = userData => dispatch => {
    Axios.post('/users/login', userData)
        .then( res => {
            // retrieve token from the response 
            const token = res.data.token;
            // console.log(token);
            // pass the token in session
            sessionStorage.setItem("jwtToken", token);
            // set the auth token
            setAuthToken(token);

            // decode the auth token
            const decoded = jwt_decode(token);
            // pass the decoded token
            dispatch(setCurrentUser(decoded))
            history.push('/dashboard');

        })
        .catch(err => {
            if(err.response.data){
                console.log(err.response)
                dispatch({
                    type: GET_ERRORS,
                    payload: err.response.data
                })
            }
        })
}

export const getUser = () => {
    return (dispatch) => {
        return Axios.get('/users/current_user',{
        }).then( res => {
            const data = res.data
            dispatch({type: GET_CURRENT_USER, data})
        })
    }
}

export const setCurrentUser = (decoded, dispatch) => {
    return{
        type:SET_CURRENT_USER,
        payload:decoded,

    }

}

相反,它似乎没有重新呈现,因为我收到一个错误,该错误只有在用户未登录时才应该有错误。

例如

TypeError: Cannot read property 'user' of undefined

在我的组件之一上。

在仪表板页面上刷新时,错误消失了,因此我正在寻找一种方法来重新呈现状态,并重定向到仪表板,使用户存在,或者如果提供了错误的凭据,则会引发未经授权的错误。

Navbar.js

import React, {Component} from "react";
import {BrowserRouter, Link, Route, Switch} from "react-router-dom";
import PrivateRoute from '../components/PrivateRoute';
import Home from '../components/Home';
import Dashboard from '../components/Dashboard';
import {connect} from 'react-redux';
import Login from '../components/Login';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import {logoutUser} from '../actions/authActions';
import SignUp from "../components/SignUp";
import Grid from '@material-ui/core/Grid';
import {createBrowserHistory} from 'history';

// import createBrowserHistory from 'history/createBrowserHistory'

export const history = createBrowserHistory()

class Navbar extends Component {
    logout = (e) => {
        e.preventDefault();
        this.props.logoutUser();

    }
    render() {
        // LINKS
        const authLinks = (
            <span>
                <Button>
                    <Link to="/dashboard" color="primary">
                        Dashboard
                    </Link>
                </Button>       
                <Button onClick={this.logout} to="/">
                    <span>Logout</span>
                </Button>
            </span>

        );
        const guestLinks = (
            <span>
                <Button>
                    <Link to="/login">
                        Login
                    </Link>
                </Button>

                <Button>
                    <Link to="/signup">
                       Sign Up
                    </Link>

                </Button>
            </span>
        );
        return (
            <div>
                <BrowserRouter history={history}>

                    <AppBar position="static">
                        <Toolbar>
                        <Grid justify="space-between" container >
                            <Typography variant="h6" style={{ color: '#fff'}}>
                               Image Upload App
                            </Typography>                         

                             {/* if is authenticated, will render authlinks 
                                if not will render guest links
                            */}
                            <Grid item>
                                <Button align="right">
                                    <Link style={{ color:'#fff'}} underline="none" to="/">
                                        Home
                                    </Link>
                                </Button>
                                 {this.props.auth.isAuthenticated ? authLinks : guestLinks}

                            </Grid>




                        </Grid>
                        </Toolbar>
                    </AppBar>
                    <Switch>
                        <Route exact path="/" component={Home}/>
                        <Route exact path="/signUp" component ={SignUp}/>
                        <Route exact path="/login" component={Login}/> {/* private routes for users who are authenticated */}
                        <PrivateRoute exact path="/dashboard" component={Dashboard}></PrivateRoute>

                    </Switch>
                </BrowserRouter>
            </div>
        )
    }
}

const mapDispatchToProps = (dispatch) => ({
    logoutUser: () => dispatch(logoutUser())
})
const mapStateToProps = (state) => ({
    auth: state.auth
})
export default connect(mapStateToProps,mapDispatchToProps)(Navbar)

App.js

import React, { Component } from 'react';
import './App.css';
import setAuthToken from "./actions/utils/setAuthToken";
import Navbar from './layout/Navbar';
import jwt_decode from "jwt-decode";
import store from './store';
import {setCurrentUser, logoutUser, getUser } from './actions/authActions';
import { Provider } from "react-redux";

// JWT TOKEN
if (sessionStorage.jwtToken) {
  // Set auth token header auth
  setAuthToken(sessionStorage.jwtToken);
  // Decode token and get user info and exp
  const decoded = jwt_decode(sessionStorage.jwtToken);
  // Set user and isAuthenticated
  store.dispatch(setCurrentUser(decoded));

  store.dispatch( getUser());

  // Check for expired token
  const currentTime = Date.now() / 1000;
  if (decoded.exp < currentTime) {

    // Logout user
    store.dispatch(logoutUser());
    // Redirect to login
    window.location.href = "/login";
  }



}
class App extends Component {
  render(){
    return (
      <Provider store={store}>
         <Navbar/>
      </Provider>
    );
  }
}
export default App;

登录

import React, { Component } from "react";
import {connect} from 'react-redux';
import {Redirect} from "react-router-dom";
import {loginUser, googleLogin } from '../actions/authActions';
import Grid from '@material-ui/core/Grid';
import PropTypes from "prop-types";
import { GoogleLogin } from "react-google-login";
import Divider from '@material-ui/core/Divider';
import Typography from '@material-ui/core/Typography';
import { GoogleLoginButton} from "react-social-login-buttons";
import LoginForm from './LoginForm/LoginForm';
import {history} from '../layout/Navbar';
import { withRouter } from "react-router-dom";
// const onSuccess = response => console.log(response);
// const onFailure = response => console.error(response);
class Login extends Component{
    constructor() {
        super();
        this.state = {
            formData:{
                username:'',
                password:'',
                isAuthenticated: false,
            },
            errors:{}
        }
    }
    logInGithub = (e) => {
        e.preventDefault();
        console.log('hello');
        this.props.githubLogin();
    }
    componentDidMount() {
        // console.log(this.props.auth);
        if (this.props.auth.isAuthenticated) {
          this.props.history.push("/dashboard");
        }
      }
    componentDidUpdate(){
        if(this.props.auth.isAuthenticated){
            this.props.history.push("/dashboard")
        }
    }

    handleChange = (e) => {
        e.preventDefault();
        const {formData} = this.state;
        this.setState({
            formData: {
                ...formData,
                [e.target.name]: e.target.value
            }
        });
    }
    handleSubmit = (e) => {
        e.preventDefault();
        const {formData} = this.state;
        const {username, password} = formData;
        const creds = {
            username,
            password
        }
        this.props.loginUser(creds,  this.props.history);


        // console.log(creds);
    }
    render(){
        const googleLogin = response => {
            let googleData;
            googleData = {
              googleID: response.profileObj.googleId,
              email: response.profileObj.email,
            //   password: "",
            };
            console.log(googleData);
            this.props.googleLogin(googleData);
          };
        return(
            <div>
            <Grid container justify="center" spacing={0}>
                <Grid item  sm={10} md={6} lg={4} style={{ margin:'20px 0px'}}>
                <Typography variant="h4" style={{ letterSpacing: '2px'}} >
                     Sign In
                </Typography>
                {this.props.auth.errors ? (
                    this.props.auth.errors.map( (err, i) => (
                        <div key={i} style={{color: 'red' }}>
                            {err} 
                        </div>
                    ))                  
                ):(
                    null
                )}
                <LoginForm 
                    mySubmit={this.handleSubmit}
                    myChange={this.handleChange}
                    username={this.state.username}
                    password={this.state.password}
                />
                    <Grid item sm={12}>
                            <Typography align="center" variant="h4" style={{ letterSpacing: '6px'}} >
                                OR
                            </Typography>
                        <Divider style={{ width: '200px', margin:'20px auto', backgroundColor:'#000000'}} variant="middle" />
                    </Grid>

                </Grid>
            </Grid>
            </div>
        )
    }
}
Login.propTypes = {
    loginUser: PropTypes.func.isRequired,
    auth: PropTypes.object.isRequired,
    errors: PropTypes.object
};
const mapStateToProps = (state) => ({
    auth: state.auth
})
const mapDispatchToProps = (dispatch) => ({
    loginUser: (userData) => dispatch(loginUser(userData)),
    googleLogin: (userData) => dispatch(googleLogin(userData))
})
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(Login))

5 个答案:

答案 0 :(得分:1)

我认为在push操作有时间完成之前,正在执行Dashboard组件的setCurrentUser()

dispatch(setCurrentUser(decoded))
history.push('/dashboard');

这两行代码正在运行,而不是像出现时那样同步执行(一个接一个)。

TypeError: Cannot read property 'user' of undefined

这可能来自您的Dashboard组件。您可能以某种身份使用了this.props.auth.user之类的东西,其中this.props.auth尚未生效。

我们可以将您的代码配置为具有更同步的操作流程。

您可能正在调用您内部的loginUser()动作创建者 登录组件(有意义)。让我们确保至少从withRouter引入react-router-dom,从您的reducer引入auth-state。通过引入这两个,我们可以在componentDidUpdate()

中处理重定向

因此,登录组件至少将使用以下导入和生命周期方法,例如:

import React from "react"
import { connect } from "react-redux"
import { loginUser } from "../../actions/authActions"
import { withRouter } from "react-router-dom"

class Login extends React.Component{
    componentDidMount(){
        if(this.props.auth.isAuthenticated){
            this.props.history.push("/dashboard")
        }
    }

    componentDidUpdate(){
        if(this.props.auth.isAuthenticated){
            this.props.history.push("/dashboard")
        }
    }

    onSubmit = (event) => {
        event.preventDefault()

        const userData = {
            email: this.state.email,
            password: this.state.password
        }

        this.props.loginUser(userData)
     }

    render(){
        return(
           ...yourmarkup
        )
    }
}

const mapStateToProps = (state) => {
    return{
        auth: state.auth
    }
}


const mapDispatchToProps = (dispatch) => {
    return{
        loginUser: (userData) => {
            dispatch(loginUser(userData))
        }
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(withRouter(Login))

请注意,在我们的 authActions 文件中,由于我们正在调用history.push("/dashboard")组件中的重定向,因此我们可以删除Login行。

同步流为:

  1. 您在Login组件内提交登录表单,然后调用 loginUser()动作创建者。
  2. loginUser执行,它调度setCurrentUser,然后我们将 解码的用户。
  3. 您的reducer现在使用经过身份验证的用户返回新状态。
  4. Login组件从redux-store接收新的道具,导致 要重新渲染的组件。
  5. componentDidUpdate()被触发,我们验证 auth-state 具有已验证的用户。如果为true,我们将重定向到Dashboard组件。

答案 1 :(得分:1)

所以看来我没有使用导航栏history

所以不是this.props.history.push("/dashboard")

我使用了history.push('/dashboard')

Login.js

  .....
  componentDidMount() {
        // console.log(this.props.auth);
        if (this.props.auth.isAuthenticated) {
          history.push("/dashboard");
        }
      }
   componentDidUpdate(){
        if(this.props.auth.isAuthenticated){
            history.push("/dashboard")
        }
    }

Navbar.js

并更改此

export const history = createBrowserHistory()

对此

export const history = createBrowserHistory({forceRefresh:true})

这不使用withRouter()实例。

我仍然必须从history.push authActions.js函数中删除loginUser

答案 2 :(得分:0)

如果您正在使用“反应> 16.8.0”和功能组件,请尝试此操作。

SBC

在功能组件中使用:

import { useHistory } from "react-router-dom";

答案 3 :(得分:0)

最新的ReactRouter(5.2)和最新的history(5.0)之间目前存在一个compatibility issue

此不兼容的症状之一是history.push()调用未触发重新渲染。将history降级为v4应该可以解决此问题。

答案 4 :(得分:0)

可以使用withRouter hoc然后使用props.history -

import { withRouter } from 'react-router'

const Navbar = props => {
   return (
     <div onClick={() => props.history.push('your-path')}>
        Navigate to your-path
     </div>
   )
}

export default withRouter(Navbar)