Reputation: 2259
I am having a hard time getting react router v4 to push a programmatic history based on an if statement conditional. All of the routing works fine on a button click, but I simply cannot get the history to work. The error that I am getting is:
TypeError: Cannot read property 'push' of undefined
This is at the line
self.props.history.push('/picturemain')
So clearly, in my component it doesn't think that history is defined. I'm not sure what I am doing wrong however. Here is my routing page:
import React from 'react'
import { Switch, Route, BrowserRouter as Router } from 'react-router-dom'
import { injectGlobal, ThemeProvider } from 'styled-components'
import { HomePage, AboutPage, PictureSwapper, PostsPage, Posts1, Posts2, Posts3, Posts4, PostsArchive, MessagePage, PictureMain } from 'components'
const App = () => {
return (
<ThemeProvider theme={theme}>
<Router>
<div>
<Route path="/" component={Posts3} exact />
<Route path="/home" component={HomePage} exact />
<Route path="/about" component={AboutPage} exact />
<Route path="/posts" component={PostsPage} exact />
<Route path="/pictureswapper" component={PictureSwapper} exact />
<Route path="/message" component={MessagePage} exact />
<Route path="/postpages/1" component={Posts1} exact />
<Route path="/postpages/2" component={Posts2} exact />
<Route path="/postpages/3" component={Posts3} exact />
<Route path="/postpages/4" component={Posts4} exact />
<Route path="/postsarchive" component={PostsArchive} exact />
<Route path="/picturemain" component={PictureMain} exact />
</div>
</Router>
</ThemeProvider>
)
}
export default App
And here is my page with the conditionals that push to history. The conditionals successfully get to the backend, and the error is only thrown because history is undefined.
import React, { Component, PropTypes } from 'react'
import { Badge, IconButton, Heading, Paragraph, ParagraphHolder,
PrimaryNavigation,
SecondaryNavigation, TertiaryNavigation,
Caption, Link, ParagraphSmall, AlignContainer,
SubHeader, PictureMain} from 'components'
import renderIf from 'render-if'
import styled from 'styled-components'
import axios from 'axios'
import {
BrowserRouter as Router,
Route,
Redirect,
Switch
} from 'react-router-dom';
class PictureLogin extends Component{
static propTypes = {
history: PropTypes.object.isRequired
}
constructor(props){
super(props);
this.state = {
name: '',
password: '',
redirect: ''
}
}
registerClick(e){
e.preventDefault();
var self = this;
axios.post('http://localhost:5000/register', {
name: this.state.name,
password: this.state.password
})
.then((response)=>{
console.log('response from the python call ', response.data);
if(response.data === 'usersuccessfullyadded'){
self.props.history.push('/picturemain')
}
})
.catch(()=>{
console.log('python axios error');
});
}
loginClick(e){
e.preventDefault();
var self = this;
axios.post('http://localhost:5000/login', {
name: this.state.name,
password: this.state.password
})
.then((response)=>{
console.log('response from the python call ', response.data);
if(response.data === 'passwordsmatch'){
console.log(self.props)
self.props.history.push('/picturemain')
}
})
.catch((err)=>{
console.log('python axios error');
console.log('and the error is ', err);
});
}
resetRedirect(){
this.setState({
redirect: ""
})
}
render(){
return(
<FlexContainer>
<FlexRow>
<div>Login</div>
<input value={this.state.name}
onChange={(e)=>{this.setState({name: e.target.value})}}
type="username" name="username" placeholder="User Name"/><br/>
<input value={this.state.password}
onChange={(e)=>{this.setState({password: e.target.value})}}
type="password" name="password" placeholder="Password"/><br/>
<button onClick={(e)=>this.registerClick(e)}>Register</button>
<button onClick={(e)=>this.loginClick(e)}>Login</button>
</FlexRow>
</FlexContainer>
)
}
}
export default PictureLogin
FlexContainer and FlexRow are my own styled components which are not declared here. That's ok as the styling is in this sheet, I just took it out for readability.
EDIT: Someone linked to a possible dupe here: How to navigate dynamically using react router dom. The fix was supposed to be that I add this to the header import {withRouter} from 'react-router-dom';
and export like this export default withRouter(PictureLogin);
. I've made those changes and there IS a change - now I am routing, but to a blank page! So...I'm going to leave this question open as there is something clearly going wrong. The page I am routing to has renderable data, but it is not rendering for some reason. This is not a fix that works.
The component I am rendering is a simple hello world to make sure that I am not including anything weird that would break it. Here is the component that I am trying to route to:
import React, { Component } from 'react'
import renderIf from 'render-if'
import styled from 'styled-components'
import axios from 'axios'
class PictureMain extends Component{
constructor(props){
super(props);
this.state = {
}
}
render(){
return(
<div>
hello world
</div>
)
}
}
export default PictureMain
Upvotes: 1
Views: 1091
Reputation: 2259
I had to do the following for my PictureMain page in my routes. I'm not sure why, but it now works. I included it in this way:
import PictureMain from './pages/picturepages/PictureMain'
This is frustrating as the others could be included in the braces, but not this one. Hmm... Anyway I'm closing as I've got the solution now. Thanks for the help everyone.
Upvotes: 0
Reputation: 3529
As far as I understand from your code, PictureLogin
is not used like <Route path="/" component={PictureLogin} exact />
If that is the case, I think <Route />
component passes history, match and location
to the components that are used as <Route path="/" component={someComponent} />
For other components, if you want to access history
you can wrap your component with an HOC called withRouter
You can import it like:
import { withRouter } from 'react-router-dom'
after that if you update your export default as
export default withRouter(PictureLogin)
there shouldn't be any Cannot read property 'push' of undefined
Upvotes: 1