I’m having some trouble with the React useState
hook. I have a list of recipe ingredients already stored in state/array called modalIngredients
That array looks like this when I log it:
[
{
"raw": "1 (4-5) pound chuck roast",
"name": null,
"quantity": 1,
"unit": ""
},
{
"raw": "salt + pepper",
"name": null,
"quantity": 0,
"unit": ""
},
{
"raw": "2 tablespoons olive oil",
"name": null,
"quantity": 2,
"unit": "tablespoon"
}
]
I am trying to update the name
, quantity
and unit
for each item in the array/state by calling a function called (parseIngredient
) that parses the raw field and spits out a result that looks like this for each ingredient in the array
{
quantity: 300,
quantityText: '300',
minQuantity: 300,
maxQuantity: 300,
unit: 'gram',
unitText: 'g',
ingredient: 'flour'
}
Where I have been able to get so far is to map the modalIngredients array and log the ingredient
, unit
, quantity
output from the parseIngredient
function and log these out to the console. This works and returns the data I am hoping to use to update in the modalIngredients
array.
Here is what this code looks like:
{modalIngredients.map((modalIngredient) => {
const parsedIngredient = parseIngredient(modalIngredient.raw, 'en')
return (
console.log(parsedIngredient.ingredient),
console.log(parsedIngredient.unit),
console.log(parsedIngredient.quantity)
)
})}
So this is where I start to struggle as I have never had to update all the items in an existing state/array before.
From what I have read I need to create a copy of the modalIngredients
state/array using spread?
And then update all the name
/quantity
/unit
for all the items in the new/copied array I created.
2
Answers
No, that’s only the case if you’re modifying your
modalIngredients
using in-place methods on your array (such as.slice()
,arr[index] = newValue
, etc.) which modify the array itself, which you normally want to avoid in React. Since.map()
returns a new array rather than modifying the existing array, you don’t need to copy the original state using the spread syntax.Instead, what you can do is create copies of the objects inside of your state array by using the spread syntax, and then update and overwrite old properties with their new values (see code comments bellow):
Notice that in the above code, nothing is updating your original
modalIngredients
array or the objects within it. The.map()
produces a new array, and the object literal{ }
with the spread...
syntax creates new objects. This is important as in React you should keep your state as readonly and thus avoid directly updating arrays via indexes (arr[i] =
) or objects via dot-notation (obj.foo =
) / bracket notation (obj['foo'] =
).Note: Your original mapping code appears to be written in curly brackets
{
}
, which suggests you’re writing this inside of JSX that you’re returning from your component. If that is the case, you’ll need to perform this mapping earlier on in your component, as you cannot have an array of objects used directly within your JSX code.There are several possible ways to update the stored values.
But you can solve this problem easily by getting the index of a specific object that you want to update.
For example:
Now, write a function to update the values.
This approach creates a new array by mapping over each item in the prevItems array. If the current item matches the criteria, it creates a new object with the same properties as the current item, but with the updated "name" property.
By doing this, we are ensuring that we are working with a new array, which is required for React to recognize the state change and rerender the component accordingly.