Reputation: 33
I have a button which adds a dropdown select based on the number of array item set in my state.
I am able to add a new dropdown, however i am having difficulties updating the state with their value for each dropdown. Every time i select the dropdown and change the option, the state reverts back to only have 1 item in the array.
any ideas of on how i am able to set the state with their current values for each dropdown that i have?
state = {
food: [
{name: 'Apple', quantity: 1}
],
};
const addFood = this.state.food.map((item, idx) => {
return
<Dropdown type="food"
className="dropdown-food"
label='food'
key={idx}
value={this.state.food[idx].name}
change={(event) => this.setState({ food: [{ name: event.target.value, quantity: this.state.food[idx].quantity }] })}
/>
})
<Button styleName="add" label="Add Food" clicked={this.addAdditionalFood}/>
Upvotes: 1
Views: 647
Reputation: 490
I fixed your problem. Now the code works as expected: 1. When a click occurs on the button, a new dropdown and a new element in the state are added. 2. When the value on the dropdown changes, its state change accordingly.
To fix the problem I changed code only in App.js file. Here the full code:
import React, { Component } from "react";
import Dropdown from "./Dropdown";
import Button from "./Button";
class App extends Component {
state = {
food: [{
name: "Apple",
quantity: 1
}]
};
addAdditionalFood = () => {
const newState = {...this.state,
food : [...this.state.food,{
name: "Apple",
quantity: 1
} ]};
this.setState(newState);
};
selectFood = (event, index) => {
const newState = {...this.state,
food : [
...this.state.food.slice(0, index),
{
...this.state.food[index],
name: event.target.value,
},
...this.state.food.slice(index + 1)
]};
this.setState(newState);
}
render() {
const addFood = this.state.food.map((item, idx) => {
return (
<Dropdown
type="food"
label="food"
key={idx}
value={this.state.food[idx].name}
change={(event) => this.selectFood(event, idx)}
/>
);
});
return (
<div className="App">
{addFood}
<Button label="Add Food" clicked={this.addAdditionalFood} />
</div>
);
}
}
export default App;
Here the code to add an additional state for a new dropdown:
addAdditionalFood = () => {
const newState = {...this.state,
food : [...this.state.food,{
name: "Apple",
quantity: 1
} ]};
this.setState(newState);
};
To ensure the immutability of the state, it is good practice not to make changes directly to the state, but manipulating the data of shallow copies. In this piece of code, I tell to do a shallow copy of the state, a shallow copy of food and, finally, to add to the food shallow copy a new value. At the end, I set the new state.
Here the code to change the state of a dropdown:
selectFood = (event, index) => {
const newState = {...this.state,
food : [
...this.state.food.slice(0, index),
{
...this.state.food[index],
name: event.target.value,
},
...this.state.food.slice(index + 1)
]};
this.setState(newState);
}
This function takes as input the event and the index of the dropdown; the event contains the new value assumed by the dropdown - this value must be entered in the state. In this piece of code, I tell to do a shallow copy of the state, a rearranged shallow copy of food. The shallow copy is so composed: it's equal to original before and after the index position and different at the index position. The element at the index position is a shallow copy of the original, but it differs from the original by the name. At the end, I set the new state.
Here the code rendering the dropdown:
<Dropdown
type="food"
label="food"
key={idx}
value={this.state.food[idx].name}
change={(event) => this.selectFood(event, idx)}
/>
Now it doesn't contain logic, but only a lambda. Don't define logic inside jsx, but use lambda at class level instead.
If change the quantity for the fruits, I suggest to wrap dropdown and input box together and pass to this new component two functions: selectFood and selectQuantity.
note: I apologize from my previous answers and them bugs. Now, the code works and apply correctly immutability.
Upvotes: 1