简体   繁体   中英

Update React Grandparent State from onClick in Grandchild

I'm trying to use react to create a list of elements, and update the state of the parent of this list when a single element is clicked.

The overall container is App.jsx (the grandparent)

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      selectedClass: null,
      query: "cs 2110"
    }

    this.handleSelectClass.bind(this);
  }

  handleSelectClass(classId) {
    console.log(classId);

    //get the id of the course and get full course details
    Meteor.call('getCourseById', classId, function(error, result) {
      if (!error) {
        console.log(this.state); 
        this.setState({selectedClass: result, query: ""}, function() {
          console.log(this.state.selectedClass);
          console.log(this.state.query);
        });
      } else {
        console.log(error)
      }
    });
  }

  //check if a class is selected, and show a coursecard only when one is.
  renderCourseCard() {
    var toShow = <div />; //empty div
    if (this.state.selectedClass != null) {
      toShow = <CourseCard course={this.state.selectedClass}/>;
    }
    return toShow;
  }

  render() {
    return (
      <div className="container">
        <header>
          <h1>Todo List</h1>
        </header>
        <div className='row'>
          <input />
          <Results query={this.state.query} clickFunc={this.handleSelectClass}/>
        </div>
        <div className='row'>
          <div className="col-md-6">
            {this.renderCourseCard()}
          </div>
          <div className="col-md-6 panel-container fix-contain">
            <Form courseId="jglf" />
          </div>
        </div>
      </div>
    );
  }
}

The parent container is Results.jsx

export class Results extends Component {
  constructor(props) {
    super(props);
  }

  renderCourses() {
    if (this.props.query != "") {
      return this.props.allCourses.map((course) => (
        //create a new class "button" that will set the selected class to this class when it is clicked.
        <Course key={course._id} info={course} handler={this.props.clickFunc}/>
      ));
    } else {
      return <div />;
    }
  }

  render() {
    return (
      <ul>
        {this.renderCourses()}
      </ul>
    );
  }
}

and the Course list item is a grandchild component

export default class Course extends Component {
  render() {
    var classId = this.props.info._id;
    return (
      <li id={classId} onClick={() => this.props.handler(classId)}>{this.props.info.classFull}</li>
    );
  }
}

I followed the suggestions here Reactjs - How to pass values from child component to grand-parent component? to pass down a callback function, but the callback still does not recognize the state of the grandparent. console.log(this.state) in App.jsx returns undefined even though the classId is correct, and the error says "Exception in delivering result of invoking 'getCourseById': TypeError: this.setState is not a function"

Is this a problem with the binding? I've tried this without Course as its own component and have the same issue.

Quickly looking through the code. I can see that problem one lies here. Even though you've bounded your function to your component, you're using a meteor call that scopes the result in it's own function scope which means that it won't be able to access this.setState. You can use fat arrow function to get around this problem, but you need to make sure that you are using ES6.

Meteor.call('getCourseById', classId, function(error, result) => {
  if (!error) {
    console.log(this.state); 
    this.setState({selectedClass: result, query: ""}, function() {
      console.log(this.state.selectedClass);
      console.log(this.state.query);
    });
  } else {
    console.log(error)
  }
});

TO

Meteor.call('getCourseById', classId, (error, result) => {
  if (!error) {
    console.log(this.state); 
    this.setState({selectedClass: result, query: ""}, () => {
      console.log(this.state.selectedClass);
      console.log(this.state.query);
    });
  } else {
    console.log(error)
  }
});

You've also binded your function incorrectly to your component.

this.handleClassSubmit = this.handleClassSubmit.bind(this);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM