Reputation: 19496
I've got a form that looks like:
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
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
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