Reputation: 1734
I have the following component with a function which can dynamically add New elements in my form. I have a button and when clicking on the button, a new form field will be added. Now I need to keep this new field's value in a new state and send it to the API. To send all the new fields, I planned to set a single state as array and push new field values after enter them. After adding few new fields, I need something like this,
this.state.other [
{
description: "field 1 description",
amount: "120$"
},
{
description: "field 2 description",
amount: "180$"
},
{
description: "field 3 description",
amount: "156$"
},
]
This is what I have tried so far:
let dataId = 0;
class ClientIntakeModal extends Component {
constructor(props) {
super(props);
this.allOtherCosts = [];
this.state = {
otherCosts: this.allOtherCosts,
other: [],
}
this.otherDescInputHandle = this.otherDescInputHandle.bind(this);
}
appendData = () => {
dataId = dataId + 1;
this.allOtherCosts.push(
<div className="halfWidth jobCostMain" key={dataId}>
<div>
<div className="jobDescAdded">
<TextField
id={'costDescription'}
name={'description'}
type="text"
value={this.state.other.description}
onChange={this.otherDescInputHandle}
/>
</div>
<div className="jobCostAdded">
<TextField
id={'costAmount'}
name={'amount'}
type="number"
value={this.state.other.amount}
onChange={this.otherDescInputHandle}
/>
</div>
</div>
</div>
);
this.setState({
otherCosts: this.allOtherCosts
});
}
otherDescInputHandle(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
other: [
...this.state.other,
{
[name]:value
},
]
});
}
render() {
return(
<div>
<div id="addNewUser" className="addNewUser" onClick={this.appendData}>
<span>+</span>
</div>
{this.allOtherCosts}
</div>
)
}
}
The problem is, I'm receiving something like follows
this.state.other [
{
description: "f",
},
{
description: "fi",
},
{
description: "fie",
},
{
description: "fiel",
},
]
How can I get the correct state here?
Upvotes: 0
Views: 3722
Reputation: 6721
In general I recommend the following approach to update a collection's item immutable. It is important to use id for the items, which can be a dynamically generated unique id.
class App extends React.Component {
state = {
someArray: [
{ id: 1, value: 'a' },
{ id: 2, value: 'b' },
{ id: 3, value: 'c' }
]
}
setValue = (id, value) => {
this.setState(prevState => ({
someArray: prevState.someArray.map(item => (
id === item.id ? { ...item, value } : item
))
}))
}
render(){
return (
<div>
<ul>
{this.state.someArray.map(({value}) => <li>{value}</li>)}
</ul>
<button onClick={() => this.setValue(2, 'foo')}>Set value</button>
</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: 1
Reputation: 68
i have some considerations to your code and that will better him. otherDescInputHandle function, use desctruction, like that:
const { target, value, name } = event;
and do that:
const { other } = this.state;
/* your ideia */
this.setState({ other });
In your appendData function, in dataId get the length of array
dataId = this.allOtherCosts.length;
and in onChange of your textfield, pass the event and the dataId
onChange={e => this.otherDescInputHandle(e, dataId)}
so, in the otherDescInputHandle you have the position of the field on array, and when you go to change the value, you cand access the correctly position of array:
const { other } = this.state;
other[dataId].description = value;
Try this ideia. I guess this will solve your problem.
And please, try to use only state variables and manipulating they. Is perfectly possible to solve your problem without using this.allOtherCosts = [];
Upvotes: 1