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

Time:09-25

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);
}

CodePudding user response:

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) => {
    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;

}

CodePudding user response:

you need to change your code to


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


The reason is because on the dropdown component you named the prop onDropdown

When you are trying to console.log your state after doing setState do it like so

      const data = response.data;
      this.setState({dbArticles: data},() => console.log('state', this.state));
    }) 

the reason is that setState is asynchronous and the second argument is a callback that fires after state has actually been set.

CodePudding user response:

You are not receiving any props in the component as you are not allowing for the parameter and also acting on the incorrect prop name:

const Dropdown = () => { // missing props here
  const [selectValue, setValue] = useState("")
    
  const handleSelect = (e) => {
    console.log(e.target.value);
    setValue(e.target.value);
    this.props.handlePractice(e.target.value); // problem here
}

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

    )
  }
  export default Dropdown;

Update to receive the props:

const Dropdown = (props) => {
  const [selectValue, setValue] = useState("")
    
  const handleSelect = (e) => {
    console.log(e.target.value);
    setValue(e.target.value);
    props.onDropdown(e.target.value); // note correct name
}

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

    )
  }
  export default Dropdown;

Codesandbox Demo

  • Related