N P
N P

Reputation: 2619

Pass data from child to parent in React

I have 3 components in React, one acts as a container which passes down my child components to be rendered in a form. When the form is submitted I want to get each of the child components in my parent component, loop through each one, create an object my server expects then send the list of objects back to the server. I am struggling to access the child components in my onSubmit function in my parent component.

Here is my parent component

ParentFixturesComponent.js

class ParentFixturesComponent extends Component {

    constructor() {
        super();
        this.state = {
            numChildren: 0,
        };
    }

    onAddMatch() {
        this.setState({
            numChildren: this.state.numChildren + 1
        });
    }

    onSubmit(e) {
        e.preventDefault();
        // loop through the child components
        // create a match object with them
        // var match = {
        //     id: uuid(),
        //     title: uuid(),
        //     start: e.something,
        //     end: e.something,
        // };
        console.log("submit works");
    }

    render() {
        const children = [];
        for (let i = 0; i < this.state.numChildren; i += 1) {
            children.push(<SingleMatchForm key={uuid()}/>)
        }

        return (
            <Fixtures addMatch={this.onAddMatch.bind(this)} save={this.onSubmit.bind(this)} >
                {children}
            </Fixtures>
        );
    }

}

export default ParentFixturesComponent;

Component which holds my form where my parent renders all my children components

ChildFixturesContainer.js

class Fixtures extends Component {


    constructor(props) {
        super(props);
        this.handleChange = this.handleChange.bind(this);
    }

    handleChange(name) {
        this.console.log(this.props);
    }

    render() {
        return (
            <div className="tray tray-center">
                <div className="row">
                    <div className="col-md-8">
                        <div className="panel mb25 mt5">
                            <div className="panel-heading">
                                <span className="panel-title">Fixtures</span>
                            </div>
                            <div className="panel-body p20 pb10">
                                <div id="fixture-parent" onChange={this.handleChange.bind(this)}>
                                    {this.props.children}
                                </div>
                            </div>
                            <div className="section-divider mb40" id="spy1"> </div>
                            <button className="btn btn-primary tm-tag" onClick={this.props.addMatch}>Add Match</button>
                            <button className="btn btn-alert tm-tag" onClick={this.props.save}>Save</button>
                        </div>
                    </div>
                </div>
            </div>
        );
    }

}

export default Fixtures;

And finally my child individual form component.

SingleMatchComponent.js

class SingleMatchForm extends Component {

    constructor() {
        super();
        this.state = {
            startDate: moment()
        };
    }

    handleChange(date) {
        this.setState({
            startDate: date
        });
    }

    render() {
        return (
            <div className="row">
                <div key={this.props.id} className="form-group">
                    <label className="control-label col-md-2">New Match</label>
                    <div className="col-md-6">
                        <DatePicker
                            selected={this.state.startDate}
                            onChange={this.handleChange.bind(this)}/>
                        <div className="section-divider mb40" id="spy1"> </div>
                    </div>
                </div>
            </div>
        );
    }

}

export default SingleMatchForm;

Upvotes: 3

Views: 14347

Answers (2)

Will Luce
Will Luce

Reputation: 1841

Essentially, you're asking about the difference between props and state.

There are two types of data that control a component: props and state. props are set by the parent and they are fixed throughout the lifetime of a component. For data that is going to change, we have to use state. Docs

This also addresses smart/dumb (or logic/display or container/presenter) components.

Your parent component should hold all of the state and the logic for changing it, then pass everything down to the other components as props. Your child components shouldn't really even have a state they just handle displaying it and, when something happens, they just send that call back up to the parent component to do whatever needs to be done.

When the child components call those handlers via props, they'll execute in the parent, updating the state there. Then you can just collect the state in the parent and submit it.

class TheParent extends component {

  constructor() {
    super();
    this.state = {
      someState: 0,
      someMoreState: 1,
      evenMoreState: 2
    };
    autoBind(this) // <- use autobind so you don't have to list them all out
  }

  updateSomeState() {
        // do something
  }

  updateSomeMoreState() {
        // do something
  }

  updateEvenMoreState() {
        // do something
  }

  onSubmit() {
    // get state and submit
  }

  render () {
    return (
      <Component1 
        someProp={this.state.someState} 
        handler={this.updateSomeState} 
      />
      <Component2 
        someProp={this.state.someMoreState} 
        handler={this.updateSomeMoreState} 
      />          
      <Component2 
        someProp={this.state.evenMoreState} 
        handler={this.updateEvenMoreState} 
      />
    )
  }

}

Upvotes: 5

errata
errata

Reputation: 6041

Define a function in your parent and send it to the child, call the parent function in child component using props. Something like:

Parent

class ParentFixturesComponent extends Component {

    // don't forget to bind the function in parent
    constructor() {
        super();
        this.state = {
            numChildren: 0,
        };
        this.someFunctionHere = this.someFunctionHere.bind(this);
    }

    someFunctionHere(param) {
        // do whatever you need
    }

    render() {
        const children = [];
        for (let i = 0; i < this.state.numChildren; i += 1) {
            children.push(<SingleMatchForm key={uuid()}/>)
        }

        return (
            <Fixtures
                addMatch={this.onAddMatch.bind(this)}
                save={this.onSubmit.bind(this)}
                someFunctionHere={this.someFunctionHere}
            >
                {children}
            </Fixtures>
        );
    }
}

export default ParentFixturesComponent;

Child

class Fixtures extends Component {

    // constructor and other stuff...

    childFunctionHere() {
        this.props.someFunctionHere(params);            
    }

    render() {
        return (
            <div id="fixture-parent" onChange={this.childFunctionHere}>
        );
    }
}

export default Fixtures;

Upvotes: 13

Related Questions