I have a select ponent that I would like to use to update the values of an array of objects based on the index.
I use the hook like this:
const [areas, setAreas] = useState(product.areas);
This returns the following for "areas":
[
0: {de: "Getraenke", en: "Drinks"},
1: {de: "Snacks", en: "Snacks"}
]
My select ponent looks like this:
{areas?.map((area: {de: string, en: string}, index: number) => (
<Wrapper key={index}>
<Select
label="Area"
name="product-area"
value={area[lang] || ''}
onChange={e => setArea([...area, { [lang]: e.target.value }])}
>
{areaOptions.map(option => (
<option value={option} key={option}>
{option}
</option>
))}
</Select>
</InputWrapper>
))}
In this way I unfortunately get the following for "area" after selecting an option (here "Sweets"):
[
0: {de: "Getraenke", en: "Drinks"},
1: {de: "Snacks", en: "Snacks"}
2: {en: "Sweets" }
]
So the first object of the array is not updated as intended, but a further object is appended, which unfortunately also lacks the "de" language.
My goal is to update the first object (or the object based on the index of the select ponent) of the array so that instead of:
0: {de: "Getraenke", en: "Drinks"}
..the updated object looks like this:
0: {de: "Suessigkeiten", en: "Sweets"}
The objects within the array should therefore be updated based on the index of the current select ponent and also take into account the selected language (default "en").
I have a select ponent that I would like to use to update the values of an array of objects based on the index.
I use the hook like this:
const [areas, setAreas] = useState(product.areas);
This returns the following for "areas":
[
0: {de: "Getraenke", en: "Drinks"},
1: {de: "Snacks", en: "Snacks"}
]
My select ponent looks like this:
{areas?.map((area: {de: string, en: string}, index: number) => (
<Wrapper key={index}>
<Select
label="Area"
name="product-area"
value={area[lang] || ''}
onChange={e => setArea([...area, { [lang]: e.target.value }])}
>
{areaOptions.map(option => (
<option value={option} key={option}>
{option}
</option>
))}
</Select>
</InputWrapper>
))}
In this way I unfortunately get the following for "area" after selecting an option (here "Sweets"):
[
0: {de: "Getraenke", en: "Drinks"},
1: {de: "Snacks", en: "Snacks"}
2: {en: "Sweets" }
]
So the first object of the array is not updated as intended, but a further object is appended, which unfortunately also lacks the "de" language.
My goal is to update the first object (or the object based on the index of the select ponent) of the array so that instead of:
0: {de: "Getraenke", en: "Drinks"}
..the updated object looks like this:
0: {de: "Suessigkeiten", en: "Sweets"}
The objects within the array should therefore be updated based on the index of the current select ponent and also take into account the selected language (default "en").
Share Improve this question asked Dec 21, 2020 at 13:27 Codehan25Codehan25 3,01412 gold badges58 silver badges109 bronze badges 1-
1
In the
onChange
, you can write any logic you like... why don't you take "areas" array and modify/create a new array the way you want, and then callsetAreas
? What am I missing? – Dima G Commented Dec 21, 2020 at 13:40
2 Answers
Reset to default 11The easiest way to do this is to clone the array, update the specific array item by index and then replace the old array with it using useState, like this.
const updateArea = (e, lang, index) => {
const updatedAreas = [...areas];
updatedArea[index][lang] = e.target.value;
setAreas(updatedAreas);
}
...
{areas?.map((area: {de: string, en: string}, index: number) => (
<Wrapper key={index}>
<Select
label="Area"
name="product-area"
value={area[lang] || ''}
onChange={e => updateArea(e, lang, index)}
>
{areaOptions.map(option => (
<option value={option} key={option}>
{option}
</option>
))}
</Select>
</InputWrapper>
))}
Let's solve this step by step
1. A further object is appended
That's because you're telling it to do so :)
onChange={e => setArea([...area, { [lang]: e.target.value }])}
This means, copy the entire array ...area
and add a new object at the end { [lang]: e.target.value }
Another mistake you're making is not using a callback function, proper way to access current ...area
should be this:
onChange={e => setArea((currentArea) => [...currentArea, { [lang]: e.target.value }]}
This way you always have the most up-to-date version of area
.
Now, to update the specific object by the index, you would do the following:
onChange={e => setArea((currentArea) => {
const { value } = e.target;
const newArea = currentArea.map((singleArea, areaIndex) => {
if (areaIndex === index) {
return { [lang]: value }
}
return singleArea;
});
return newArea;
}}
2. Lacks the "de" language
Again, you're explicitly only adding one language to it :)
{ [lang]: value } // { en: 'something' }
Not sure how to fix this one for you, but at least you can understand why its wrong, It should be something like (just a concept, this won't work):
{ [langDe]: value, [langEn]: value } // { de: 'Guten Tag', en: 'something' }