clattenburg cake
clattenburg cake

Reputation: 1222

React - TypeError: Cannot read property 'push' of undefined, already exported default withRouter

I have a page component SimulationReport that I want to redirect to upon clicking a button from the Reports page component. This is the App component with the Router:

function App() {
  return (
    <React.Fragment>
      <Router>
        <NavigationBar />
        <Sidebar />
        <Switch>
          <Route exact path="/" component={Ratings} />
          <Route path="/LeagueSettings" component={LeagueSettings} />
          <Route path="/Fixtures" component={Fixtures} />
          <Route path="/Reports" component={Reports} />
          <Route path="/SimulationReport" component={SimulationReport} />
        </Switch>
      </Router>
    </React.Fragment>
  );
}

And here are the relevant code bits from Reports which is a function component:

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

<td>
   <Button variant="primary" onClick={handleSimView(index)}>
   View
   </Button>
</td>

  const handleSimView = index => e => {
    console.log("index: " + index);
    loadSimulationResult(simulationIds[index]);
    props.history.push("/SimulationReport");
  };

And at the end of the file I have this:

export default withRouter(Reports);

Upon clicking the button, I receive this error:

TypeError: Cannot read property 'push' of undefined

I'm not sure what I need to do to redirect this? Or maybe I should be doing it a different way?

Upvotes: 0

Views: 96

Answers (1)

Alan Maldonado
Alan Maldonado

Reputation: 133

Seems that your prop history is not being passed from the <Route> tag in your component. You can pass props to a Route Rendered Component by doing the following line:

<Route
 path='/SimulationReport'
 render={(props) => <SimulationReport {...props} history={yourHistoryObjectForTheRouter} />}
/>

where yourHistoryObjectForTheRouter would be your custom history exported object.

EDIT: Updated code

UPDATE TO ANSWER TO ADDRESS OP's NEEDS:

1° Alright, so first you'll need to install the package called history to your React Application. (You can use npm i history or yarn add history. It depends on what you have installed. If you don't know what's yarn then use the npm option)

2° Somewhere in your project inside the App folder (src/App/), you're going to create another folder called history and inside index.js. The contents of the index.js file are going to be the next ones.

import { createBrowserHistory } from 'history';

export default createBrowserHistory({});

3° Once that is done, head to your App/index.js file, where you have your <Router> main tag. If you've done everything as I stated before, in that App/index.js you're going to add a new import:

import history from "./history";

Which is your new Custom History Object.

Here you can choose two paths of how you want to solve your problem

=== First Solution

4° Scroll down in that same file, until you find your <Router></Router> tag, and you're going to update it to this next part:

<Router>
   <NavigationBar />
    <Sidebar />
    <Switch>
      <Route exact path="/" component={Ratings} />
      <Route path="/LeagueSettings" component={LeagueSettings} />
      <Route path="/Fixtures" component={Fixtures} />
      <Route path="/Reports" component={Reports} />
      <Route
          path='/SimulationReport'
          render={(props) => <SimulationReport {...props} history= 
              {history} />}
      />
    </Switch>
</Router>

After this, you'll have your Custom History Object setted up and your component now should work. This solution however will only work for that specific component Route, and that means that you will have to do the same for the other <Route > that require the use of the history object.

Stop here if you choose this solution.

=== Second Solution

This solution is better, because you'll now have access to your history object globally, just with an import.

4° Scroll down in that same file, until you find your <Router></Router> tag, and you're going to update it to this next part:

<Router history={history}> //Here's the change
   <NavigationBar />
    <Sidebar />
    <Switch>
      <Route exact path="/" component={Ratings} />
      <Route path="/LeagueSettings" component={LeagueSettings} />
      <Route path="/Fixtures" component={Fixtures} />
      <Route path="/Reports" component={Reports} />
      <Route path="/SimulationReport" component={SimulationReport} />
    </Switch>
</Router>

After this, you'll have your Custom History Object setted up.

5° Navigate to the file that contains the Reports functional component, and on the imports at the top of your file, you're going to import the same history as you did in App/index.js. Beware that depending on the level of subfolders that your Reports components is in, it's how the import is going to change. It can end up like this:

import history from "../history"; or this import history from "../../history"; it depends or even more "../". It depends on your subfolder levels

After that, you'll need to update your function handleSimView so instead of doing this:

const handleSimView = index => e => {
    console.log("index: " + index);
    loadSimulationResult(simulationIds[index]);
    props.history.push("/SimulationReport");
  };

do this:

const handleSimView = index => e => {
    console.log("index: " + index);
    loadSimulationResult(simulationIds[index]);
    history.push("/SimulationReport"); //Change is here, removed "props."
  };

After this, your code should work. This solution you can implement it everywhere as you would only need to import the history object and just use it.

I'll be waiting to hear from you to see if it worked. If anything else happens, you can ask me.

Upvotes: 1

Related Questions