cjm2671
cjm2671

Reputation: 19496

React, pulling state out of components

I've got a form that looks like:

enter code here

At the bottom is the relevant part of the form state, cast to JSON, which is in this component.

When you click 'Add another', it adds another pair of fields. These field pairs are a child component, CountryFieldRow.

In the child component, when the fields are edited, the child's state is updated properly.

How do I get the state to update in the parent object, so I can update the JSON printed?

Edit, code added:

import React, { Component } from 'react';
import CountryFormRow from "./CountryFormRow"

class CountryForm extends React.Component {
  constructor(props) {
    super(props)
    this.state = {rules: props.rules}
    this.handleDelete = this.handleDelete.bind(this)
    this.handleAdd = this.handleAdd.bind(this)
    this.handleInputChange = this.handleInputChange.bind(this)

  }
  handleInputChange(event) {
    console.log(event)
    this.setState({
      [event.target.name]: event.target.value
    });
  }

  handleDelete(event) {
    this.setState({rules: this.state.rules.slice(0, -1)})
    console.log(rules)
  }

  handleAdd(even) {
    this.setState({rules: this.state.rules.concat({what: "country"})})
  }

  handleChange(event){
    console.log(event)
  }


  render() {
    // var my_json = JSON.stringify(this.state.rules)
    // console.log(my_json)

    var controls = this.state.rules.map((rule,index)=> 
      <CountryFormRow key={index} id={rule.id} url={rule.url} matches={rule.matches} what={rule.what} onUpdate={this.handleChange} />
    )

    var buttons =
      <div className="form-group row">
        <div className="col-sm-12">
          <div className="pull-right">
            {this.state.rules.length > 0 &&
            <a onClick={this.handleDelete} className="btn btn-white btn-sm">
              Remove
            </a>
            }
            <a onClick={this.handleAdd} className="btn btn-white btn-sm">
              + Add Another
            </a>
          </div>
        </div>
      </div>

    var json_state =
      <div>
        {JSON.stringify(this.state.rules)}
      </div>

    return([controls, buttons, json_state])

  }
}
export default CountryForm;

Child object:

import React, { Component } from 'react';

class CountryFormRow extends React.Component {
  constructor(props) {
    super(props)
    this.state = {url: props.url, matches: props.matches, what: props.what}
    this.handleInputChange = this.handleInputChange.bind(this)
  }

  handleInputChange(event) {
    this.setState({
      [event.target.name]: event.target.value
    });
  }

  render() {
    return (
      <div className="geo-field">
        <div className="form-group row">
          <input id="link_dd_0_what" name="link[dd_0_what]" type="hidden" value="this.props.country"/>
          <label className="col-sm-2 col-form-label">Country</label>
          <div className="col-sm-10">
            <select onChange={this.handleInputChange} className="form-control m-b" name="matches" defaultValue={this.props.matches}><option value=""></option><option value="AF">Afghanistan</option><option value="AL">Albania</option><option value="DZ">Algeria</option><option value="AS">American Samoa</option></select>
          </div>
        </div>
        <div className="form-group row">
          <label className="col-sm-2 col-form-label">Destination</label>
          <div className="col-sm-10">
            <input onChange={this.handleInputChange} className="form-control" name="url" placeholder="https://www.example.com" type="text" defaultValue={this.props.url}/>
          </div>
        </div>
      </div>
      )
  }
}

export default CountryFormRow

Upvotes: 0

Views: 525

Answers (2)

Tholle
Tholle

Reputation: 112917

Instead of having the state in CountryFieldRow and trying to sync that to the parent component state, you could keep all the state in the parent component and pass down state-altering functions to CountryFieldRow instead.

Example

class App extends React.Component {
  state = {
    rows: [
      {
        country: "Andorra",
        destination: "www.alpha.com"
      }
    ]
  };

  addRow = () => {
    this.setState(prevState => ({
      rows: [...prevState.rows, { country: "", destination: "" }]
    }));
  };

  onChangeCountry = (country, index) => {
    this.setState(prevState => {
      const rows = [...prevState.rows];
      rows[index] = { ...rows[index], country };
      return { rows };
    });
  };

  onChangeDestination = (destination, index) => {
    this.setState(prevState => {
      const rows = [...prevState.rows];
      rows[index] = { ...rows[index], destination };
      return { rows };
    });
  };

  render() {
    return (
      <div>
        {this.state.rows.map((row, i) => (
          <CountryFieldRow
            country={row.country}
            onChangeCountry={e => this.onChangeCountry(e.target.value, i)}
            destination={row.destination}
            onChangeDestination={e =>
              this.onChangeDestination(e.target.value, i)
            }
          />
        ))}
        <button onClick={this.addRow}> Add another </button>
      </div>
    );
  }
}

function CountryFieldRow(props) {
  return (
    <div>
      <input value={props.country} onChange={props.onChangeCountry} />
      <input value={props.destination} onChange={props.onChangeDestination} />
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>

Upvotes: 2

yaron1m
yaron1m

Reputation: 104

You need to manage the state in the higher component.

You should hold the pairs in the state of the higher component and pass it down to the CountyFieldRow elements.

This way you have one state with all the information you need and you can use it to update the JSON.

To update the parent state from the components you need to pass an onUpdate function to the components, one that will update the right part of the parent component state.

Was that clear enough? if not, attach some code sample and I will make it clearer

Upvotes: 0

Related Questions