skip to Main Content

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


  1. From what I have read I need to create a copy of the modalIngredients
    state/array using spread?

    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):

    const updatedIngredients = modalIngredients.map((modalIngredient) => {
      const parsedIngredient = parseIngredient(modalIngredient.raw, 'en')
      return { // <--- return a new object...
        ...modalIngredient, // <--- that has all the properties from modalIngredient...
        // v--- with these properties overwritten
        name: parsedIngredient.ingredient,
        unit: parsedIngredient.unit,
        quantity: parsedIngredient.quantity
      };
    });
    

    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.

    Login or Signup to reply.
  2. 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:

    const [modalIngredient, setModalIngredient] = useState([
        {
            "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"
        }
    ]);
    

    Now, write a function to update the values.

    const updateIngredients = () => {
       setModalIngredients((prevItems) => {
          return prevItems.map((modalIngredient) => {
             if (modalIngredient.raw === "1 (4-5) pound chuck roast") {
                return {
                   ...modalIngredient,
                   name: "ingredients",
                };
             }
             return modalIngredient;
          });
       });
    };
    

    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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search