I am trying to create dynamic fields based on selected options from drop-down list. Basically, UI looks something like this:
If I select one option UI will render in following manner:
Same goes for multiple options:
Problem is that, every time I try to change value of my dynamically generated fields, React RE-RENDERS the components and value for all fields resets. In other words, even though state is sort of changing, UI will not show anything.
Here is the code I use for rendering:
render(){
let {testDataSpec} = this.state;
const renderConstraints = (constraints, index) => {
if(constraints.length === 1){
return <Input
label_val={constraints[0][0]}
className={this.hasError("field") ? "is-invalid" : ""}
type={'text'}
name={constraints[0]+'Value'}
handleChange={e => this.handleFormChange(index, 0, e)}
errorDiv={this.hasError("field") ? "text-danger" : "d-none"}
errorMsg={"Enter Constraint Value"}
/>
}else if(constraints.length && constraints.length !== 1){
return constraints.map((constraint, idx)=>(
<Input
label_val={constraints[idx][0]}
className={this.hasError("field") ? "is-invalid" : ""}
type={'text'}
name={constraints[idx]+'Value'}
handleChange={e => this.handleFormChange(index, idx, e)}
errorDiv={this.hasError("field") ? "text-danger" : "d-none"}
errorMsg={"Enter Constraint Value"}
key={constraints[idx]+'_' + idx}
/>
))
}
}
return (
<Fragment>
<h2>Add Test Data Specification</h2>
<hr />
<form onSubmit={this.handleSubmit}>
<input type="hidden" name="id" id="id" value={testDataSpec.id} onChange={this.handleChange} />
<div className="row">
<Input
label_val={"Name"}
className={this.hasError("name") ? "is-invalid" : ""}
type={'text'}
name={'name'}
handleChange={this.handleChange}
errorDiv={this.hasError("name") ? "text-danger" : "d-none"}
errorMsg={"Please enter TDS name"}
/>
</div>
{this.state.formValues.map((element, index)=>(
<div key={index} className="row">
<div className="col-lg">
<Input
label_val={"Field Name"}
className={this.hasError("field") ? "is-invalid" : ""}
type={'text'}
name={'field'}
handleChange={e => this.handleFormChange(index, e)}
errorDiv={this.hasError("field") ? "text-danger" : "d-none"}
errorMsg={"Please enter field name"}
/>
</div>
<div className="col-lg">
<Select
label_name={"DataTypes"}
name={"dtype"}
options={this.state.dataTypes}
handleChange={e => this.handleFormChange(index, e)}
/>
</div>
<div className="col-lg">
<Select
label_name={"Constraints"}
name={"constraints"}
options={this.state.constraints}
handleChange={e => this.handleFormChange(index, null, e)}
isMultiple={true}
/>
</div>
<div className="col-lg">
{
renderConstraints(element.constraints, index)
}
</div>
<div className="col-sm">
{
index ?
<button style={{height: "50px", width: "100px", marginTop: "30px"}} type="button" className="btn btn-danger" onClick={() => this.removeFormFields(index)}>Remove</button>
:null
}
</div>
</div>
))}
<hr />
<button style={{height: "50px", width: "100px", marginRight: "20px"}} className="btn btn-success" type="button" onClick={() => this.addFormFields()}>Add</button>
<button style={{height: "50px", width: "100px"}} className="btn btn-primary">Save</button>
</form>
<div className="mt-3">
<pre>{JSON.stringify(this.state, null, 3)}</pre>
</div>
</Fragment>
)
}
When I have only 1x field selected, there is no issue and value is being changed accordingly, without React re-rendering components:
But when I have multiple fields selected, re-rendering of the components deletes the values. On the image below you can see that state is filled (Min is 2 and Max is 3), but values are not showing in the fields due to re-rendering
EDIT
handleFormChange and constructor:
constructor(props){
super(props);
this.state = {
testDataSpec: {
id: 0,
name: "",
},
formValues: [{field: "", dtype: "", constraints: {}}],
isLoaded: false,
error:null,
errors: [],
constraints: [
{"id": "Min", value: "Min"},
{"id": "Max", value: "Max"},
{"id": "Range", value: "Range"},
],
dataTypes: [
{"id": "short", value: "Int16"},
{"id": "integer", value: "Int32"},
{"id": "long", value: "Int64"},
{"id": "float", value: "Float32"},
{"id": "double", value: "Float64"},
{"id": "string", value: "String"},
]
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
hasError(key){
return this.state.errors.indexOf(key) !== -1;
}
addFormFields(){
this.setState(({
formValues: [...this.state.formValues, {field: "", constraints: []}]
}))
}
removeFormFields(i){
let formValues = this.state.formValues;
formValues.splice(i, 1);
this.setState({formValues});
}
handleFormChange(i, c, e) {
let formValues = this.state.formValues;
if (e.target.name === "constraints"){
//let map = new Map(Array.from(e.target.selectedOptions, option => [option.value, null]));
//let newIterable = map.entries(map);
//formValues[i][e.target.name] = Array.from(newIterable);
formValues[i][e.target.name] = Array.from(e.target.selectedOptions, option => [option.value, null]);
}else if(e.target.name.includes("Value")){
console.log("(i, c) = (", i, c, ")")
let obj = [
formValues[i]["constraints"][c][0],
e.target.value
]
formValues[i]["constraints"][c] = obj;
}else{
formValues[i][e.target.name] = e.target.value;
}
this.setState((prevState) => ({
...prevState,
["formValues"]: formValues,
}))
}




