Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React State update a nested array with objects based on the id when iterated

Tags:

reactjs

I have an react state with objects/array that is filled, but also then later I want to edit text inputs and edit the related fields on object also, i found no solutions until now.

So this is my state for example:

const [data, setData] = useState({
    working_hours: [
      {
        id: 1,
        description: 'Random Text',
        price: 100,

      },
      {
        id: 2,
        description: 'Text Random',
        price: 100,
      },
    ]
  });

Here is my Jsx:

{data.working_hours.map(item => 
<>
  <input type={text} value={item.description}
   onChange={(e) => handleChange(e)} />

  <input type={text} value={item.price}
   onChange={(e) => handleChange(e)} />
</>
)}

Here is what I tried:

function handleChange(e){
 const value = e.target.value;
 setData({...data, [...data.working_hours, e.target.value]})
}
like image 382
modih65067 Avatar asked Sep 07 '26 15:09

modih65067


2 Answers

You need to pass additional parameters to your handleChange as item ID which you want to update and property name because without these you will not be able to identify which property to update dynamically. This way you can use the same handleChange for multiple inputs.
See below code -

function handleChange(e, itemId, property) {
    const value = e.target.value;
    //copying data to temp variable so that we do not directly mutate original state
    const tempWorkingHours = [...data.working_hours];
    //findIndex to find location of item we need to update
    let index = tempWorkingHours.findIndex(item => item.id == itemId);
    // -1 check to see if we found that object in working hours
    if(index != -1){
       tempWorkingHours[index] = {
         ...tempWorkingHours[index], //keeping existing values in object
         [property]: value  //here property can be "price" or "description"
       }
    }
    
    setData({ ...data, working_hours: tempWorkingHours })
}

{
    data.working_hours.map(item =>
        <>
            <input type={text} value={item.description}
                onChange={(e) => handleChange(e, item.id, "description")} />

            <input type={text} value={item.price}
                onChange={(e) => handleChange(e, item.id, "price")} />
        </>
    )
}
like image 61
Sagar Darekar Avatar answered Sep 12 '26 04:09

Sagar Darekar


When you want to update the state of objects in the nested array, you must identify these objects and the property you want to update. Thus your handler should look like this.

function handleChange(index, property, value){
   // ...
}

The setData function will only trigger a rerender if you pass it a new object. Thus you should create a copy.

function handleChange(index, property, value) {
  const new_working_hours = [...data.working_hours]; // copy the array
  const new_working_hour = { ...data.working_hours[index] }; // copy the array item to change

  new_working_hour[property] = value; // set the new value
  new_working_hours[index] = new_working_hour; // assign the new item to the copied array

  setData({ working_hours: new_working_hours }); // return a new data object
}

Here is a working example. Click Run code snippet below.

const { useState } = React;

const App = () => {
  const [data, setData] = useState(initialState);

  function handleChange(index, property, value) {
    const new_working_hours = [...data.working_hours]; // copy the array
    const new_working_hour = { ...data.working_hours[index] }; // copy the array item to change

    new_working_hour[property] = value; // set the new value
    new_working_hours[index] = new_working_hour; // assign the new item to the copied array

    setData({ working_hours: new_working_hours }); // return a new data object
  }

  return data.working_hours.map((item, index) => (
    <div>
      <input
        type="text"
        value={item.description}
        onChange={(e) => handleChange(index, "description", e.target.value)}
      />

      <input
        type="text"
        value={item.price}
        onChange={(e) => handleChange(index, "price", e.target.value)}
      />
    </div>
  ));
};


const initialState = {
  working_hours: [
    {
      id: 1,
      description: "Random Text",
      price: 100
    },
    {
      id: 2,
      description: "Text Random",
      price: 100
    }
  ]
};

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
like image 40
René Link Avatar answered Sep 12 '26 02:09

René Link



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!