In the following SideMenuRow child div inside my SideMenuContainer component, I'm calling the changeInfoList method. The method is in the parent App.js.

In App.js:

<SideMenuContainer changeInfoList={this.changeInfoList} genInfoList={this.state.genInfoList} groupedObjectsList={this.state.groupedObjectsList}/>

In SideMenuContainer.js:

<SideMenuRow onClick={this.props.changeInfoList(genInfoElement.name)}>

Here are the relevant sections of the App.js code involving the method and state:

  constructor(props) {
    super(props);
    this.state = {
      genInfoList: [],
      groupedObjectsList: [],
      cardInfo: {
        name: data[0].name
      },
      data: []
    };
  }

  changeInfoList(rowName){
    //change the card rendered based on which row is clicked
    this.setState({
      cardInfo: {
        name: rowName
      }
    })
  }

What's incorrect about the way I'm structuring this method that isn't allowing setState to work?

2 Answers

2
yourfavoritedev On Best Solutions

You're getting this error because the keyword this is not pointing to your component's execution context and it's current execution context does not have a method called setState()

You can approach this a couple of ways:

1) Define an arrow function like the following:

  const changeInfoList = (rowName) => {
    //change the card rendered based on which row is clicked
    this.setState({
      cardInfo: {
        name: rowName
      }
    })
  }

By using an arrow-function (non-binding this), the this keyword now points to the closest object, in this case your class component, which has a setState() method.

2) Bind your current function inside your component's constructor.

 constructor(props) {
    super(props);
    this.state = {
      genInfoList: [],
      groupedObjectsList: [],
      cardInfo: {
        name: data[0].name
      },
      data: []
    };

 this.changeInfoList = this.changeInfoList.bind(this)
  }

This explicitly tells your function that this is now pointed to your class component's context.

Now assuming you did either of these steps, you would then have to pass down changeInfoList into your child components:

In App.js:

<SideMenuContainer changeInfoList={this.changeInfoList} genInfoList={this.state.genInfoList} groupedObjectsList={this.state.groupedObjectsList}/>

In SideMenuContainer:

<SideMenuRow onClick={() => { this.props.changeInfoList(genInfoElement.name) }}>
1
Community On
 constructor(props) {
    super(props);
    this.state = {
      genInfoList: [],
      groupedObjectsList: [],
      cardInfo: {
        name: data[0].name
      },
      data: []
    };
// Bind the function
this.changeInfoList = this.changeInfoList.bind(this)
  }

  changeInfoList(rowName){
    //change the card rendered based on which row is clicked
    this.setState({
      cardInfo: {
        name: rowName
      }
    })
  }