Uncaught TypeError: Cannot read property 'push' of undefined (React-Router-Dom)
JavascriptReactjsReact RouterReact Router-DomJavascript Problem Overview
I have a Dashboard with rotating slides, each of which has a corresponding tab in Bldgs. Both Dashboard.js
and Bldgs.js
are children to my App.js
.
When a user clicks on a specific slide A
in Dashboard.js
, Dashboard
needs to tell App.js
so that App
can tell Bldgs.js
to have tab A
displayed when it routes to Bldgs
.
I believe that I am passing the correct index value from Dashboard
up to App
and down to Bldgs
. However, an error is being thrown in my App.js
file stating:
Uncaught TypeError: Cannot read property 'push' of undefined
My code was working fine before I started passing my handleClick()
function to my Dashboard
component.
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
import injectTapEventPlugin from 'react-tap-event-plugin';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import { BrowserRouter as Router } from 'react-router-dom';
import { hashHistory } from 'react-router';
// Needed for onTouchTap
// http://stackoverflow.com/a/34015469/988941
injectTapEventPlugin();
ReactDOM.render(
<MuiThemeProvider>
<Router history={hashHistory}>
<App />
</Router>
</MuiThemeProvider>,
document.getElementById('root')
);
App.js
import React from 'react';
import { Route } from 'react-router-dom';
import Dashboard from './Dashboard';
import Bldgs from './Bldgs';
var selectedTab;
class App extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
selectedTab = 0;
}
handleClick(value) {
selectedTab = value;
// console.log(selectedTab);
this.props.history.push('/Bldgs');
// console.log(this.props);
}
render() {
var _this = this;
return (
<div>
<Route exact path="/" render={(props) => <Dashboard {...props} handleClick={_this.handleClick} />} />
<Route path="/Bldgs" component={Bldgs} curTab={selectedTab} />
</div>
);
}
}
export default App;
Dashboard.js
import React, { Component } from 'react';
import './Dashboard.css';
import { AutoRotatingCarousel, Slide } from 'material-auto-rotating-carousel';
...
var curIndex;
class Dashboard extends Component {
constructor(props) {
super(props);
this.handleEnter = this.handleEnter.bind(this);
this.handleChange = this.handleChange.bind(this);
curIndex = 0;
}
handleEnter(e) {
// console.log(curIndex);
this.props.handleClick(curIndex);
}
handleChange(value) {
// console.log(value);
curIndex = value;
}
...
}
export default Dashboard;
Bldgs.js
...
var curTab;
class Bldgs extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.goHome = this.goHome.bind(this);
curTab = 0;
}
handleChange(value) {
this.setState({'selectedTab': value});
console.log(this.state);
}
goHome(e) {
this.props.history.push('/');
}
...
}
export default Bldgs;
Javascript Solutions
Solution 1 - Javascript
In order to make use of history
in the App
component use it with withRouter
. You need to make use of withRouter
only when your component is not receiving the Router props
,
This may happen in cases when your component is a nested child of a component rendered by the Router or you haven't passed the Router props to it or when the component is not linked to the Router at all and is rendered as a separate component from the Routes.
import React from 'react';
import { Route , withRouter} from 'react-router-dom';
import Dashboard from './Dashboard';
import Bldgs from './Bldgs';
var selectedTab;
class App extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
selectedTab = 0;
}
handleClick(value) {
selectedTab = value;
// console.log(selectedTab);
this.props.history.push('/Bldgs');
// console.log(this.props);
}
render() {
var _this = this;
return (
<div>
<Route exact path="/" render={(props) => <Dashboard {...props} handleClick={_this.handleClick} />} />
<Route path="/Bldgs" component={Bldgs} curTab={selectedTab} />
</div>
);
}
}
export default withRouter(App);
Documentation on withRouter
Solution 2 - Javascript
for React-router V4 change the function to
onClick={this.fun.bind(this)}
fun() {
this.props.history.push("/Home");
}
and
import { withRouter } from 'react-router-dom';
export it later as:
export default withRouter (comp_name);
Solution 3 - Javascript
When working with functional components we can use useHistory() to history to push method
import { useHistory } from "react-router-dom";
and then in function we have to assign the useHistory()
let history = useHistory();
Now We can use history.push to relocate to desired page
history.push('/asdfg')
Solution 4 - Javascript
You are trying to push with out giving the task to a valid library. on App.js
So: on App.js
You are using BrowserRouter which is handling the history of pages by default, with this react-router-dom function you are relying on BrowserRouter to do this fore you.
Use Router instead of BrowserRouter to gain control of you're history, use history to control the behavior.
- Use npm history "yarn add [email protected]" / "npm i [email protected]"
- import Route from 'react-router-dom'; //don't use BrowserRouter
- import createBrowserHistory from 'createBrowserHistory';
Remember to exoport it !! export const history = createBrowserHistory();
4.import history to Dashboard.js 5.import history to Bldgs.js
hope this helps !!! @brunomiyamotto
Solution 5 - Javascript
See my App.js file below. I ended up passing {...this.props}
to the NavBar Component that I had created.
The NavBar component is not part of my Switch routes.
import React, { Component } from 'react';
import { Route, Link, Redirect, Switch, withRouter } from 'react-router-dom'
import Navbar from './Navbar.js';
import Home from './Home/HomeCont';
import Login from './Register/Login';
import Dashboard from './Dashboard/DashboardCont';
import './App.css';
class App extends Component {
constructor(props){
super(props);
}
render() {
return (
<div className="App">
<header className="App-header">
//SOLUTION!!!
<Navbar {...this.props}/>
</header>
<Switch>
<React.Fragment>
<Route exact path="/" render={(props) => <Home {...props}/>}/>
<Route exact path="/login" render={(props) => <Login {...props}/>}/>
</React.Fragment>
</Switch>
</div>
);
}
}
export default withRouter(App);
Within my Navbar. The button I used had the following code. I had to .bind(this) to see the history and be able to user this.props.history.push("/")
<Button
color="inherit"
onClick={this.handleLogout.bind(this)}>Logout</Button>
Solution 6 - Javascript
My mistake was the wrong import in conjuction with BrowserRouter
, ie:
incorrect:
import { useHistory } from 'react-router'
correct:
import { useHistory } from 'react-router-dom'
Solution 7 - Javascript
I solved this error by wrapping the component inside the BrowserRouter.
Don't forget about this, it's a very common mistake.
import { BrowserRouter} from 'react-router-dom';
<BrowserRouter>
<Menu/>
<BrowserRouter>
Only the router childrens receive the history hook.