Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I update states `onChange` in an array of object in React Hooks

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>
)
like image 589
reddy Avatar asked May 05 '19 00:05

reddy


People also ask

How do you update state arrays in React hooks?

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.

How do you update array of objects State in React?

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.

How do you update state with objects in React?

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.


Video Answer


2 Answers

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>
)
like image 124
Steffan Avatar answered Oct 27 '22 08:10

Steffan


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 
))
like image 97
spender Avatar answered Oct 27 '22 09:10

spender