Home > Back-end >  TypeError: Cannot read properties of undefined (reading 'push') with dropdown select in Re
TypeError: Cannot read properties of undefined (reading 'push') with dropdown select in Re

Time:12-04

Sorry if this is a dumb question but I'm trying to get my react chops up and build a simple site with a select dropdown which should route to a URL when selected. However I'm getting this error. Or if there's a better way to do this with hooks, I am all ears :). Thanks

×
TypeError: Cannot read properties of undefined (reading 'push')
DropDown.onChange
src/components/Dropdown.js:10
   7 | 
   8 | class DropDown extends Component {
   9 |   onChange = (e) => {
> 10 |     this.props.history.push(`/${e.target.value}`);
  11 |   }
  12 | 
  13 |   render() {

Below is my code

   import React, { Component } from 'react'
   import '../App.css'
   import ReactDOM from 'react-dom';
   import { BrowserRouter, Route, withRouter } from "react-router-dom";

   class DropDown extends Component {
   onChange = (e) => {
    this.props.history.push(`/${e.target.value}`);
  }

   render() {
     return (
      <div>
        <label htmlFor="pests">Choose a pest:</label>
        <select onChange={this.onChange}>
          <option value="ants">Ants</option>
          <option value="wasps">Wasps</option>
        </select>
      </div>
    )
  }
}

export default DropDown;

Here's my router code

function App() {
  return (
    <div className="main">
      {/* <Navbar/> */}
      <Header />
      <NavbarBasic />
      <Dropdown />
      {/* These are the main routes and subpages */}
      <Routes>
        <Route path='/' element={<Home />} />
        <Route path='/about' element={<About />} />
        <Route path='/pests-we-treat' element={<Pests />} />
        <Route path='/services' element={<Services />} />
        <Route path='/contact' element={<Contact />} />
        <Route path='/ants' element={<Ants />} />
        <Route path='/wasps' element={<Wasps />} />
      </Routes>
    </div>
  );
}

export default App;

CodePudding user response:

Class component to functional component

Transform your class component in a functional componente and import useHistory like this :

import { useHistory } from "react-router-dom";
...
let history = useHistory();
history.push(`/${e.target.value}`);

Another way, using JS

    import React, { Component } from 'react';
    // import { BrowserRouter, Route, withRouter } from "react-router-dom";

    class DropDown extends Component {

      onChange = (e) => {
        // this.props.history.push(`/${e.target.value}`);
        window.location.href = `/${e.target.value}`;
      }

      render() {
        return (
            <div>
              <label htmlFor="pests">Choose a pest:</label>
              <select onChange={this.onChange}>
                <option value="ants">Ants</option>
                <option value="wasps">Wasps</option>
              </select>
            </div>
        );
      }
    }

    export default DropDown;

Another method mixing class component and React Hooks

dropdownRouter.tsx

    import { createBrowserHistory } from 'history';

    export default function DropdownPush(path: string) {
      let history = createBrowserHistory();
      history.push(`/${path}`);
    }

OR

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

    export default function DropdownPush(path: string) {
       const history = useHistory();
       history.push(`/${path}`);
    }

OR if you are using v6 react-router-dom useHistory() was replaced by useNavigate()

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

    export default function DropdownPush(path: string) {
      const navigate = useNavigate();
      navigate(`/${path}`);
    }

dropdown.tsx

    import React, { Component } from 'react';
    import DropdownPush from './dropdownRouter';

    class DropDown extends Component {
      constructor(props) {
        super(props);
      }

      onChange = (e) => {
        // this.props.history.push(`/${e.target.value}`);
        
        // JS redirect method
        // window.location.href = `/${e.target.value}`;
        
        // Using Hook
        DropdownPush(e.target.value);
      };

      render() {
        return (
          <div>
            <label htmlFor="pests">Choose a pest: </label>
            <select onChange={this.onChange}>
              <option value="ants">Ants</option>
              <option value="wasps">Wasps</option>
            </select>
          </div>
        );
      }
    }

    export default DropDown;

CodePudding user response:

I expect you want to wrap your Dropdown component in the withRouter higher-order component. That would usually happen in the export:

import { withRouter } from "react-router";

class DropDown extends Component {
  // ...
}

export default withRouter(Dropdown);

The alternative way to access history is to use the useHistory hook, and the docs contain a good example of how to use it.

  • Related