2

How do I call a child component function from the parent component? I've tried using refs but I can't get it to work. I get errors like, Cannot read property 'handleFilterByClass' of undefined.

Path: Parent Component

 export default class StudentPage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
    };
  }
  newStudentUserCreated() {
    console.log('newStudentUserCreated1');
    this.refs.studentTable.handleTableUpdate();
  }

  render() {
    return (
      <div>
        <StudentTable
          studentUserProfiles={this.props.studentUserProfiles}
          ref={this.studentTable}
        />
      </div>
    );
  }
}

Path: StudentTable

export default class StudentTable extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
     studentUserProfiles: props.studentUserProfiles,
    };

    this.handleTableUpdate = this.handleTableUpdate.bind(this);
  }

  handleTableUpdate = () => (event) => {
    // Do stuff
  }

  render() {
    return (
      <div>
       // stuff
      </div>
    );
  }
}

UPDATE Path StudentContainer

export default StudentContainer = withTracker(() => {
  const addStudentContainerHandle = Meteor.subscribe('companyAdmin.addStudentContainer.userProfiles');
  const loadingaddStudentContainerHandle = !addStudentContainerHandle.ready();
  const studentUserProfiles = UserProfiles.find({ student: { $exists: true } }, { sort: { lastName: 1, firstName: 1 } }).fetch();
  const studentUserProfilesExist = !loadingaddStudentContainerHandle && !!studentUserProfiles;

  return {
    studentUserProfiles: studentUserProfilesExist ? studentUserProfiles : [],
  };
})(StudentPage);
bp123
  • 3,217
  • 8
  • 35
  • 74
  • 1
    What do you want to accomplish? It's probably a better idea to pass down the `studentUsers` as a prop to the `StudentTable` rather than try to call a function in a child component. – Tholle Jul 17 '18 at 22:36
  • Oh, I already pass the studentUsers down as a prop. Basically, another component is inserting a new studentUser. When this happens I need to tell the table to update, so the new studentUser will appear without the page needing to be reloaded. – bp123 Jul 17 '18 at 22:39
  • Alright. If another component is updating `studentUsers`, then it should update down the tree of components automatically. How are you updating `studentUsers`? – Tholle Jul 17 '18 at 22:41
  • Do you mean within StudentTable component? – bp123 Jul 17 '18 at 22:42
  • I was thinking of the component up the tree, the parent to `StudentPage` that updates the `studentUserProfiles`. – Tholle Jul 17 '18 at 22:43
  • Oh, no. My design here is. Student form component (Child 1) creates new studentProfile -> StudentPage (Parent component) is notified of a newUser through the newStudentUserCreated function which then tells -> StudentTable component (Child 2) to run a function to update the state of the table data. Is that the wrong way to do it? – bp123 Jul 17 '18 at 22:46
  • You are passing `studentUserProfiles` to `StudentTable` with `studentUserProfiles={this.props.studentUserProfiles}`, i.e. `StudentPage` must be getting the `studentUserProfiles` from its parent component. – Tholle Jul 17 '18 at 22:48
  • Sorry, I see the confusion. I'm using Meteor js. Meteor recommends loading data in a container and then passing the data into the Page as a prop. See update above. – bp123 Jul 17 '18 at 22:53
  • FYI. Meteor kinda takes care of passing the new props down the wire. That's why I just need to tell the studenttable state to update. – bp123 Jul 17 '18 at 22:55
  • I'm not very good at meteor, but if the props coming from meteor to `StudentPage` changes, it will cause a re-render of `StudentPage`. This re-render will in turn re-render `StudentTable` which would update the table. There is something I don't understand. In any case, it's not "the React way" to expose functions from child components to call in the parent components. Communications should happen through props, or passing functions to child components that can be called to update something in the parent component. – Tholle Jul 17 '18 at 22:58
  • This is what I'm trying to do `You can send a call back form child1(ie page with form) to parent and send it as a prop from parent to child2(ie page with table).` Is this possible? – bp123 Jul 17 '18 at 23:05
  • I don't think I can be of much help, sadly. You should not try to send data between sibling components. I think [this part of the documentation is a great read on the subject](https://reactjs.org/docs/lifting-state-up.html). – Tholle Jul 17 '18 at 23:12
  • Possible duplicate of [Call child method from parent](https://stackoverflow.com/questions/37949981/call-child-method-from-parent) – Nisarg Shah Sep 03 '19 at 07:23

1 Answers1

3

My design here is: component (Child 1) creates a new studentProfile. Parent component is notified ... which then tells component (Child 2) to run a function to update the state of the table data.

I'm paraphrasing the OP's comment here but it seems the basic idea is for a child component to update a sibling child.

One solution is to use refs.

In this solution we have the Parent pass a function to ChildOne via props. When ChildOne calls this function the Parent then via a ref calls ChildTwo's updateTable function.

Docs: https://reactjs.org/docs/refs-and-the-dom.html

Demo (open console to view result): https://codesandbox.io/s/9102103xjo

class Parent extends React.Component {
    constructor(props) {
        super(props);
        this.childTwo = React.createRef();
    }
    newUserCreated = () => {
        this.childTwo.current.updateTable();
    };
    render() {
        return (
            <div className="App">
                <ChildOne newUserCreated={this.newUserCreated} />
                <ChildTwo ref={this.childTwo} />
            </div>
        );
    }
}

class ChildOne extends React.Component {
    handleSubmit = () => {
        this.props.newUserCreated();
    };
    render() {
        return <button onClick={this.handleSubmit}>Submit</button>;
    }
}

class ChildTwo extends React.Component {
    updateTable() {
        console.log("Update Table");
    }
    render() {
        return <div />;
    }
}
wdm
  • 7,121
  • 1
  • 27
  • 29