Search code examples
javascriptreactjsreact-props

Error: Cannot read properties of undefined (reading 'props') in ReactJS


I can not seem to get props working for passing information from my child class up to my parent class. What I am trying to do, is to detect what dropdown menu option someone has chosen, send that information up to the parent class which then takes the chosen option and runs a call to backend that then returns a list of database items which match the chosen option.

Below is the child class:

import React,{useState} from 'react';
import SEPractices from "../dummydata/SEPractices"

  const optionItems = SEPractices.map((SEPractice) =>
                <option key={SEPractice.practice}>{SEPractice.practice}</option>
            );
  const Dropdown = () => {
  const [selectValue, setValue] = useState("")
    
  const handleSelect = (e) => {
    console.log(e.target.value);
    setValue(e.target.value);
    this.props.handlePractice(e.target.value);
}

    return (
        <div>
             <select value={selectValue} onChange={handleSelect}> 
                
               <option value="dummyData">
                 Select an SE Practice
                 </option>
                {optionItems}
             </select>
         </div>

    )
  }
  export default Dropdown;

Below is the parent class:

import React,{useState} from "react";
import Styles from "../components/tablestyle.js";
import Table from "../components/evidencetable.js";
import tablecolumns from "../components/tablecolumns.js";
import Dropdown from "../components/Dropdown.js";
import axios from "axios";

//const dbArticles = [];
class SEPractice extends React.Component{
  /*constructor(props){
  this.state = {dbArticles: []};
  this.componentDidMount = this.componentDidMount.bind(this);
  this.getArticles = this.getArticles.bind(this);
  this.practice = this.practice.bind(this);
  }*/
  state={
    dbArticles: []
  }
  
  handlePractice (Option) {
  
    console.log("hi");

    // axios.get(`https://protected-plains-77403.herokuapp.com/api/articles/${Option}`)
    axios.get(`https://localhost:5000/api/articles/${Option}`)
    .then((response) =>{
      const data = response.data;
      this.setState({dbArticles: data});
      console.log('state', this.state);
    })
    .catch(()=>{
      alert('error retrieving data');
    });
  }

  componentDidMount = () =>{
    this.getArticles();
  }
  getArticles = () =>{
    axios.get('https://protected-plains-77403.herokuapp.com/api/articles')
    .then((response) =>{
      const data = response.data;
      this.setState({dbArticles: data});
      console.log('state', this.state);
    })
    .catch(()=>{
      alert('error retrieving data');
    });
  }
/*practice =() =>{
  const [selected, setSelected] = useState('');
  const practiceHandler = (e) =>{
    setSelected(e);
  }
}*/
render() {
    return (

      <div>
        <h2>Select SE Practice to get evidence for the claimed benefits</h2>
            <Dropdown onDropdown={this.handlePractice}/>
            <Styles>
                <Table
                data={this.state.dbArticles}
                columns={tablecolumns}
                />
            </Styles>
      </div>
    );
}
}
export default SEPractice;  

I followed a few articles and stack overflow blogs and they all say to do this.props.method which I did:

  const handleSelect = (e) => {
    console.log(e.target.value);
    setValue(e.target.value);
    this.props.handlePractice(e.target.value);
}

Solution

  • In the parent class, you are not correctly binding the method in the constructor. You need the following:

    class SEPractice extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
           dbArticles: []
        }
    
        this.handlePractice = this.handlePractice.bind(this);
      }
    

    The handlePractice method is now bound to the parent component. When you pass it to the child component, you assign it to the property onDropdown. You need to then access it correctly from the child component using the onDropdown property to which you have assigned it. Note, because the child component is a functional component, we do not use this.props, like you did in the parent component which is a class component. Like so:

      const Dropdown = (props) => { //Notice here we passed props into the function that generates the component
        const [selectValue, setValue] = useState("")
        
        const handleSelect = (e) => {
          console.log(e.target.value);
          setValue(e.target.value);
          props.onDropdown(e.target.value); //Not this.props
        }
    
        return (
            <div>
                 <select value={selectValue} onChange={handleSelect}> 
                    
                   <option value="dummyData">
                     Select an SE Practice
                     </option>
                    {optionItems}
                 </select>
             </div>
    
        )
      }
      export default Dropdown;
    
    }