64

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;
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
Mike
  • 1,307
  • 3
  • 17
  • 29
  • 2
    https://stackoverflow.com/questions/42123261/programmatically-navigate-using-react-router-v4/42124328#42124328 This has the answer you need. – Chaim Friedman May 16 '17 at 19:11

7 Answers7

115

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

Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • This small change made everything work, but could you please explain what it's doing? Thank you! – Mike May 16 '17 at 19:52
  • Added the withRouter documentation, which describes this. – Shubham Khatri May 17 '17 at 03:57
  • Hey Guys,Now a days we import 'withRouter' from "react-router" not from "react-router-dom" . Documentation - https://v5.reactrouter.com/web/api/withRouter – Aravin Jan 04 '22 at 06:17
26

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);
Dragos Strugar
  • 1,314
  • 3
  • 12
  • 30
cryptoKTM
  • 2,593
  • 22
  • 21
26

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')
Markus Rohlof
  • 380
  • 3
  • 13
Aaquib
  • 404
  • 8
  • 17
  • 1
    OP is using class component, so it is not possible to use hooks inside it + back then, when question was asked, there was no possibility to use hooks as they didn't exist. – Michał Tkaczyk Oct 30 '20 at 11:19
  • 9
    I know but I also wanted to provide solution for people who now use hooks too, this answer is asked 3 years ago, now people have moved to functional component more, and the question doesn't specify about class component only, So if now people find this question, hope they find my solution for hooks too – Aaquib Oct 31 '20 at 07:08
  • 5
    I'm doing excatly like this, and that's why I'm here... – O-9 May 05 '21 at 10:19
  • 2
    It helps me a lot to solve problem. Thanks. – Ezra Lin Aug 07 '21 at 09:12
1

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.

  1. Use npm history "yarn add history@4.x.x" / "npm i history@4.x.x"
  2. import Route from 'react-router-dom'; //don't use BrowserRouter
  3. 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

1

My mistake was the wrong import in conjuction with BrowserRouter, ie:

incorrect:

import { useHistory } from 'react-router'

correct:

import { useHistory } from 'react-router-dom'

chantey
  • 4,252
  • 1
  • 35
  • 40
0

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>
Roger Perez
  • 2,807
  • 29
  • 31
0

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.

julianm
  • 2,393
  • 1
  • 23
  • 24