'How do I change the state of an array of objects in React?
I'm trying to change the state of an array containing an object whenever something is typed inside of an input. The state of the array I'm trying to change looks like this:
const defaultCV = {
personalInfo: {
firstName: "",
lastName: "",
title: "",
about: "",
},
education: [
{
id: uniqid(),
university: "",
city: "",
degree: "",
subject: "",
from: "",
to: "",
},
],
Specifically, I want to change the state of the 'education' section. My current, non-working code looks like this:
const handleEducationChange = (e) => {
setCV((prevState) => ({
...prevState,
education: [
{
...prevState.education,
[e.target.id]: e.target.value,
},
],
}));
};
When I type in the input and the function is triggered, I get the error "Warning: Each child in a list should have a unique "key" prop." I've been trying to make this work for the past few hours, any help as to what I'm doing wrong would be appreciated.
Solution 1:[1]
Are you using the Array.map() method to render a list of components? That is a common cause of that error. For example if you are mapping the education array.
You can fix by using the object id as the key since that is already generated for each object:
defaultCV.education.map(institution => {
return <Component key={institution.id} institution={institution} />
}
Solution 2:[2]
You are destructuring an array in to an object that will not work
education: [{ // this is the object you are trying to restructure into
...prevState.education, // this here is an array in your state
[e.target.id]: e.target.value,
}, ],
}));
Solution 3:[3]
Suppose you render this input field:
<input id='0' type='text' name='university' value={props.value} />
Your event.target object will include these props: {id = '0', name = 'university', value = 'some input string'}
When updating the state, you have to first find the array item (object), that has this id prop, then you can change its 'name' property and return the new state object.
This worked for me:
setCV(prevState => {
const eduObjIdx = prevState.education.findIndex(obj => obj.id === +e.target.id)
prevState.education[eduObjIdx][e.target.name] = e.target.value
return {
...prevState,
education: [
...prevState.education.splice(0, eduObjIdx),
prevState.education[eduObjIdx],
...prevState.education.splice(eduObjIdx + 1),
],
}
})
Make sure you send the current state of the input value when rendering the component:
<Component value={state.education[currentId].id} />
where currentId is the id of the university you are rendering. If you render it mapping an array, don't forget the key (answered at 0), otherwise you'll get the above error message. This way you don't mutate the whole education array. May not be the best solution though.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | quasi |
| Solution 2 | Justin Meskan |
| Solution 3 | Dharman |
