SamWhite
SamWhite

Reputation: 89

Not sure how to update parent state from the child component

I have a CreateTesterModal class which has a state of location. This class renders a form component that shows up inside this modal class like so: CreateTesterModal -> CreateTesterForm

CreateTesterModal

When user clicks on Add Location link then this new modal opens which contains the location form CreateTesterForm -> AddLocationModal -> AddLocationForm

AddLocationModal

My question now is how do I update my location state from the main modal (CreateTesterModal) based on what user will enter in the child component of AddLocationForm?

I am new to react and really need this logic to work without using redux. Can someone please help?


CreateTesterModal:

constructor(props) {
    super(props);
    this.state = {
      fields: {
        location: 'No Location Selected',
      }
    };
  }

  fieldChange(field, value) {
    this.setState(update(this.state, { fields: { [field]: { $set: value } } }));
  }

  updateLocation(newLocation) {
    this.setState({
      location: newLocation
    })
  }

  render() {
    return (
      <div>
        ...
        <div className={`modal-body ${styles['proj-modal-body']}`}>
          <CreateTesterCharacteristicForm
            fields={this.state.fields}
            onChange={this.fieldChange.bind(this)}
            onValid={() => handleSubmit(this.state.fields)}
            onInvalid={() => console.log('Error!')}
            updateLocation={this.updateLocation.bind(this)}
          />
        </div>
        ...
      </div>
    );
  }

CreateTesterForm:

<form>
    <div id={styles.locationField} className={`form-group ${styles.formGroup} ${styles.projName}`}>
              <label htmlFor="inputEmail3" className="col-sm-2 control-label">Location:</label>
              <div className="location">
                <label className="radio-inline">
                  &nbsp;
                  <Link to="#" data-toggle="modal" data-target="#addLocationModal">Add Locations</Link>
                </label>
              </div>
            </div>
    </form>
    <button
            className={`btn btn-primary text-white ${styles.saveBtn}`}
            onClick={e => {
              e.preventDefault();
              this.props.$submit(onValid, onInvalid);
            }}
          >
            Save
          </button>
<AddLocationModal />

AddLocationModal:

render() {
    return (
      <div id="newLocationModal">
        <AddLocationForm />
      </div>
    );
  }

AddLocationForm:

constructor(props) {
    super(props);
    this.state = {
      locations: [],
    };
  }
render() {
  return (
    <form id="modalForm" className="form-horizontal">
      <input
        type="text"
        class="form-control"
        id="location"
      />
      <div className={`modal-footer ${styles.modalFooter}`}>
        <button className={`btn btn-primary text-white ${styles.saveBtn}`}>
          Add More
        </button>
        <button
          type="button"
          className={`btn btn-default ${styles.cancelBtn}`}
          data-dismiss="modal"
        >
          Finish
        </button>
      </div>
    </form>
  );
}

Upvotes: 1

Views: 719

Answers (2)

Youssef
Youssef

Reputation: 110

You would have to define a function in the child component that calls another function in the parent component(as props) with the data you want to update.I don't have your code but it should be something like this:

//child component
  ...class
  update = (data) =>{
   this.props.Parentupdate(data)      
    }
  //Parentupdate is the function in the parent class that will be passed down as props to the child comp.

//Inside the parent component
   update = (data) =>{
    this.setState({data etc etc})
}
//When rendering the child component
  <Child Parentupdate={this.update}/>
//This is passing the parent function to the child as a prop.

Upvotes: 0

3Dos
3Dos

Reputation: 3487

Basically, you can do this by passing a handler from the parent to the child

Attention : Here the handler works fine with this because it has been defined as an arrow function. If using the class method syntax, don't forget to bind the this of the class to the method or this will reference the DOM element triggering the event (in this case, the button)

class Parent extends Component {
  state = { location: null }

  updateLocation = location => {
    this.setState({ location })
  }

  render () {
    return <div><Child updateLocation={this.updateLocation} /></div>
  }
}

// Get the updateLocation function as a prop in your child component
const Child = props => {
  const { updateLocation } = props

  return <button onClick={() => updateLocation('someLocation')}>Some Location</button>
}

Upvotes: 1

Related Questions