Reputation: 201
I have a Modal which contains controllers (Input
, DropDown
, Textarea
, ...) built dynamically.
I'm trying to achieve a behavior for the Input
controller onChange
method, to get the value updated inside the input once the user type something inside.
(I tried with the simple html input
and with some other libraries Input
like ant design - but same behavior).
For that purpose, I sat a name for each input controller, and sent the event to onChange
, then used setState
to set a state variable with the name of the controller with it's value:
else if (element.Type === TEXT) {
const inputName = element.Name.toLowerCase().replace(" ", "");
this.setState({ [inputName]: element.Value }, () =>
elements.push(
<td className="view-cell" align="left">
{element.Name}<br />
<Input key={element.Id}
name={inputName}
style={{ width: '100%' }}
// label={element.Name}
defaultValue={element.Value}
value={this.state[inputName]}
disabled={!element.Editable}
// size={element.Width}
onChange={(e) => {
this.onInputValueChange(e)
this.onInputChange(element.Id, element.Name, e.target.value)
}}
/>
</td>
))
}
onInputValueChange(e) {
const name = e.target.name
this.setState({
[e.target.name]: e.target.value
}, () => {
console.log(this.state[name]);
});
}
The problem is that the change is not reflected on the screen.
What can be the reason? is it the fact that the setState
is an async action?
To reproduce, you can use this sandbox (type something inside text boxes)
Upvotes: 4
Views: 2976
Reputation: 664
onInputValueChange(e, index, i) {
const name = e.target.name;
let header = this.ruleProperties.Parts.Header.Rows
header[i].Elements[index].Value = e.target.value
console.log(header[i], header[i].Elements[index], "valuessssssss");
this.setState(
{
header: this.buildRowsUsing_antD(header),
},
() => {
console.log(header, "dfsaf");
}
);
}
buildRowsUsing_antD(rows) {
let localRows = [];
let elements = [];
console.log("ressosss", rows);
rows.map((row, i) => {
elements = [];
row.Elements.map((element, index) => {
// console.log(element.Type);
if (element.Type === LIST) {
elements.push(
<td className="view-cell" align="left">
{element.Name}
<br />
<TreeSelect
// className=""
style={{ width: "100%" }}
// value={element.Value}
defaultValue={element.Value}
dropdownStyle={{
maxHeight: 400,
overflow: "auto",
zIndex: "10000",
position: "absolute"
}}
treeData={this.buildTreeData_antD(element.Values)}
// placeholder={element.Name}
// treeDefaultExpandAll
// onChange={this.onInputChange(element.Id, element.Name)}
onChange={val =>
this.onInputChange(element.Id, element.Name, val)
}
/>
</td>
);
} else if (element.Type === "Text") {
// console.log(".......", element.Value);
// -----------------------------------------------------------------------------------
let inputName = element.Name.toLowerCase().replace(" ", "");
// this.setState({ [inputName]: element.Value });
// this.setState({ [inputName]: element.Value }, () =>
elements.push(
<td className="view-cell" align="left">
{element.Name}
<br />
<Input
key={element.Id}
name={inputName}
style={{ width: "100%" }}
// className="rounded"
// label={element.Name}
// defaultValue={element.Value}
value={element.Value}//do changes hereeeeeeeeee
disabled={!element.Editable}
// size={element.Width}
onChange={e => {
this.onInputValueChange(e, index, i);//pass indexxxxxxxx
// this.onInputChange(element.Id, element.Name, e.target.value)
}}
/>
</td>
);
// );
} else if (element.Type === TEXTAREA) {
const inputName = element.Name.toLowerCase().replace(" ", "");
this.setState({ [inputName]: element.Value });
// this.setState({ [inputName]: element.Value }, () =>
elements.push(
<td className="view-cell-full" width="100%">
{element.Name}
<br />
<TextArea
key={element.Id}
name={inputName}
// className="rounded"
componentclass="textarea"
rows={5}
style={{ width: "100%", resize: "auto" }}
// placeholder=""
disabled={!element.Editable}
defaultValue={element.Value}
value={this.state[inputName]}
onChange={e => {
this.onInputValueChange(e); // this.setState({inputVal: e.target.value})
// this.onInputChange(element.Id, element.Name, e.target.value)
//TODO - Assuming that text area is only for constraint field
// this.props.ruleChangeCallBack(null, e.target.value);
}}
/>
</td>
);
// );
} else if (element.Type === BUTTON) {
elements.push(
<Button
key={element.Id}
// onClick={this.onInputChange(element.Id, element.Name, null)}
onClick={() => this.onInputChange(element.Id)}
>
{element.Value}
</Button>
);
} else if (element.Type === LABEL) {
elements.push(
<td className="view-cell-full" align="left">
{element.Value}
</td>
);
}
});
// localRows.push(<div className="view-row">{elements}</div>)
localRows.push(<tr className="view-row">{elements}</tr>);
});
return (
<table width="100%" className="view-table">
<tbody>{localRows}</tbody>
</table>
);
}
Upvotes: 1
Reputation: 2719
The issue is that you are rendering your component in a state. You have your inputs, rendered in an Array, wrapped around html, set as a state. When you are editing some input value, you are updating your state, but not the input array. Some values inside this array change, but not the state containing this array -> this state is not re-rendered and the changes are not displayed.
The easy way to fix this is to transform you input from controled
to uncontroled
fields by removing value={this.state[inputName]}
. You will be able to get the value with this.state[inputName]
, but you are no longer telling the input what content it should have (meaning you cannot, for example, parse the data like this for a phone number input +33 1 00 00 00 00
instead of the default +33100000000
.
The hard way is to re-render your array state on each change (not efficient) by editing your RuleEditor.js
:
else if (element.Type === TEXT) {
const inputName = element.Name.toLowerCase().replace(" ", "");
/*
** NO -> You don't want to set the state for each render -> infinite loop risk
*/
//this.setState({ [inputName]: element.Value })
elements.push(
<td className="view-cell" align="left">
{element.Name}
<br />
<Input
key={element.Id}
name={inputName}
style={{ width: "100%" }}
value={this.state[inputName]}
disabled={!element.Editable}
onChange={e => {
this.onInputValueChange(e);
}} />
</td>
);
onInputValueChange(e) {
const name = e.target.name;
this.setState(
{
[name]: e.target.value
},
() => {
//RE-SETSTATE FOR YOUR HEADER AND MAIN
this.setState({
header: this.buildRowsUsing_antD(this.ruleProperties.Parts.Header.Rows),
main: this.buildRowsUsing_antD(this.ruleProperties.Parts.Main.Rows)
});
}
);
}
For this, defaultValue will not be used. If you want to set a default value, you have to assign this.state[inputName]
to the default value BEFORE or AFTER rendering the state (not when you are rendering it). Although a not so good but easy way is to do value={this.state[inputName] || element.Value}
Upvotes: 2