1

I have array of objects and the format is like this

 addedOpaqueMaterials = [{
            conductivity: 1
            density: 1
            id: "1"
            ......
            ......
         },
         {
             conductivity: 2
             density: 1
             id: "2",
             ......
             ......
         }]

and from the react state i am getting updated object with the id= 2 like as this below,

{
    conductivity: 4
    density: 23
    id: "2",
    ......
    ......
}

and i am looking to update the object in main array with these values, what could be the best way to update, below is the code related to that.

const handleDrawerSubmit = values => {
   const addedOpaqueMaterials = formValues.constructionSet?.opaqueMaterials; // array of objects

   const updatedMaterial = addedOpaqueMaterials.find(i => i.id === values?.opaqueMaterial?.id);
   // values?.opaqueMaterial is having updated object
    
   // Object.assign(updatedMaterial, values.opaqueMaterial); getting an error      
};

Now I would like to merge values.Opaquematerial object into addedOpaqueMaterials which is array of objects, what could be the best way to achieve this?

Many thanks

Glory Raj
  • 17,397
  • 27
  • 100
  • 203
  • Does this answer your question? [Whats the best way to update an object in an array in ReactJS?](https://stackoverflow.com/questions/28121272/whats-the-best-way-to-update-an-object-in-an-array-in-reactjs) – Heretic Monkey Jan 06 '21 at 00:17
  • See also [Correct modification of state arrays in React.js](https://stackoverflow.com/q/26253351/215552) – Heretic Monkey Jan 06 '21 at 00:30

2 Answers2

2

This is just a regular algorithm challenge. No need to otherthink it. The only requirement that react demands out of you is that it needs to be a new array. Array#map works perfectly fine.

const materialExists = addedOpaqueMaterials.some(({id}) => id === values?.opaqueMaterial?.id)
let updatedMaterial
if (materialExists) {
    updatedMaterial = addedOpaqueMaterials.map(material => {
        if (material.id === values?.opaqueMaterial?.id) {
            return values.opaqueMaterial
        }
        return material
    })
} else {
    updatedMaterial = [...addedOpaqueMaterials, values.opaqueMaterial]
}
Andrew
  • 7,201
  • 5
  • 25
  • 34
1

Instead of using .find, use .findIndex first, so that you can replace the object in the array with the new object. (Remember not to use Object.assign in React when the first parameter is stateful, because that'd result in a state mutation)

You should also check that all of these objects exist first before trying to update.

if (!formValues.constructionSet || !values || !values.opaqueMaterial) {
  return;
}
const newMat = values.opaqueMaterial;
const index = addedOpaqueMaterials.findIndex(mat => mat.id === newMat.id);
if (index === -1) {
  // Add to the end of the existing array:
  const newOpaqueMaterials = [
    ...addedOpaqueMaterials,
    newMat
  ];
  // put newOpaqueMaterials into state
} else {
  // Replace the object in the state array with the new object:
  const newOpaqueMaterials = [
    ...addedOpaqueMaterials.slice(0, index),
    newMat,
    ...addedOpaqueMaterials.slice(index + 1)
  ];
  // put newOpaqueMaterials into state
}
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • You should modify your variable declarations. Declaring inside the `if` block will make the array unavailable where it's needed. EDIT: Sorry, I did not see your comment. Seems kind of weird, to have the same operation in an `if` and `else`, but it is viable. – Andrew Jan 06 '21 at 00:36
  • @Andrew OP didn't show how he's updating the state, or if he's using hooks or class components. He'll probably be able to replace the occurrences of `// put newOpaqueMaterials into state` with `setMaterials(newOpaqueMaterials)` or the equivalent, which doesn't require modifying the scope used in the answer. – CertainPerformance Jan 06 '21 at 00:38
  • The construction of the new array is somewhat different depending on whether the index is -1 or not. Yeah, I guess you could use the conditional operator to shove it all into a single statement (or use `let`, ew), but I really think separate blocks makes the intent of the code clearer. – CertainPerformance Jan 06 '21 at 00:40
  • Using `let` from time to time isn't the worst thing in the world. Another option is a `do` expression, but that would only be an answer if you assume they have the babel config for it. – Andrew Jan 06 '21 at 00:44