I have retrieved datas stored using useState
in an array of object, the datas was then outputted into form fields. And now I want to be able to update the fields (state) as I type.
I have seen examples on people updating the state for property in array, but never for state in an array of object, so I don't know how to do it. I've got the index of the object passed to the callback function but I didn't know how to update the state using it.
// sample data structure
const datas = [
{
id: 1,
name: 'john',
gender: 'm'
}
{
id: 2,
name: 'mary',
gender: 'f'
}
]
const [datas, setDatas] = useState([]);
const updateFieldChanged = index => e => {
console.log('index: ' + index);
console.log('property name: '+ e.target.name);
setData() // ??
}
return (
<React.Fragment>
{datas.map((data, index) => {
<li key={data.name}>
<input type="text" name="name" value={data.name} onChange={updateFieldChanged(index)} />
</li>
})}
</React.Fragment>
)
We simply, use the update method (In our example it's setMyArray() ) to update the state with a new array that's created by combining the old array with the new element using JavaScript' Spread operator.
To update an array of objects state in React: Use the map() method to iterate over the array. On each iteration, check if a certain condition is met. Update the properties of the object that matches the condition.
State can hold any kind of JavaScript value, including objects. But you shouldn't change objects that you hold in the React state directly. Instead, when you want to update an object, you need to create a new one (or make a copy of an existing one), and then set the state to use that copy.
Here is how you do it:
// sample data structure
/* const data = [
{
id: 1,
name: 'john',
gender: 'm'
}
{
id: 2,
name: 'mary',
gender: 'f'
}
] */ // make sure to set the default value in the useState call (I already fixed it)
const [data, setData] = useState([
{
id: 1,
name: 'john',
gender: 'm'
}
{
id: 2,
name: 'mary',
gender: 'f'
}
]);
const updateFieldChanged = index => e => {
console.log('index: ' + index);
console.log('property name: '+ e.target.name);
let newArr = [...data]; // copying the old datas array
// a deep copy is not needed as we are overriding the whole object below, and not setting a property of it. this does not mutate the state.
newArr[index] = e.target.value; // replace e.target.value with whatever you want to change it to
setData(newArr);
}
return (
<React.Fragment>
{data.map((datum, index) => {
<li key={datum.name}>
<input type="text" name="name" value={datum.name} onChange={updateFieldChanged(index)} />
</li>
})}
</React.Fragment>
)
The accepted answer leads the developer into significant risk that they will mutate the source sequence, as witnessed in comments:
let newArr = [...data];
// oops! newArr[index] is in both newArr and data
// this might cause nasty bugs in React.
newArr[index][propertyName] = e.target.value;
This will mean that, in some cases, React does not pick up and render the changes.
The idiomatic way of doing this is by mapping your old array into a new one, swapping what you want to change for an updated item along the way.
setDatas(
datas.map(item =>
item.id === index
? {...item, someProp : "changed"}
: item
))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With