在用户登录后,我希望重新渲染/刷新,所以我使用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))
答案 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
行。
同步流为:
Login
组件内提交登录表单,然后调用
loginUser()
动作创建者。loginUser
执行,它调度setCurrentUser
,然后我们将
解码的用户。Login
组件从redux-store接收新的道具,导致
要重新渲染的组件。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)