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>
)
Answer from naffetS on Stack Overflow
Top answer
1 of 13
232

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>
)
2 of 13
123

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.

setData(
    data.map(item => 
        item.id === index 
        ? {...item, someProp : "changed", someOtherProp: 42}
        : item 
))
🌐
React
react.dev › learn › updating-arrays-in-state
Updating Arrays in State – React
To replace an item, create a new array with map. Inside your map call, you will receive the item index as the second argument. Use it to decide whether to return the original item (the first argument) or something else: ... import { useState ...
Discussions

How to update an array by index using the useState hook?
The easiest way to do this is to clone the array, update the specific array item by index and then replace the old array with it using useState, like this. More on stackoverflow.com
🌐 stackoverflow.com
How do you update a value inside an array of objects with useState?
People already answered you here about how you use setState in your case. I would strongly suggest you to use useReducer instead of useState. It would allow you to have more complex operations tied to your state and thus, save you from writing too much code and increase readability. You could, for example, do this: dispatch({type: "update", obj: { id:3, newProperty: "hellowWorld" } }) or dispatch({type: "replace", obj: { id:3, ...properties }) Whenever your state gets too complex. Always consider using the useReducer hook . More on reddit.com
🌐 r/react
32
14
April 15, 2022
In React Hook, how can I change specific value of useState array?
Bring the best of human thought and AI automation together at your work. Explore Stack Internal ... In my project, I have to change the _value[index] when the (rating) button is clicked. So I have to dynamically change the _value[index]. If you know the answer, please let me know. Thank you! ... There are generally a couple ways to update arrays ... More on stackoverflow.com
🌐 stackoverflow.com
Component not updating when changing a element in a array in useState
NextJS Version: 12.0.7 NodeJS Version: 14.17.5 Browser: Chrome OS: macOS Describe the Bug When you create a array using useState(), and then you update one of it's elements, the component will ... More on github.com
🌐 github.com
2
3
April 27, 2022
🌐
CodingDeft
codingdeft.com › posts › react-usestate-array
How to store and update arrays in React useState hook | CodingDeft.com
December 6, 2022 - Here inside the reduce method callback, if the index is the same as that of the index to be updated, then we concatenate the previous array with an array of the number to be inserted and the current item.
Top answer
1 of 2
11

The easiest way to do this is to clone the array, update the specific array item by index and then replace the old array with it using useState, like this.

const updateArea = (e, lang, index) => {
  const updatedAreas = [...areas];
  updatedArea[index][lang] = e.target.value;
  setAreas(updatedAreas);
}

...

{areas?.map((area: {de: string, en: string}, index: number) => (
  <Wrapper key={index}>
    <Select
      label="Area"
      name="product-area"
      value={area[lang] || ''}
      onChange={e => updateArea(e, lang, index)}
    >
      {areaOptions.map(option => (
        <option value={option} key={option}>
          {option}
        </option>
      ))}
    </Select>
  </InputWrapper>
))}
2 of 2
2

Let's solve this step by step

1. A further object is appended

That's because you're telling it to do so :)

onChange={e => setArea([...area, { [lang]: e.target.value }])}

This means, copy the entire array ...area and add a new object at the end { [lang]: e.target.value }

Another mistake you're making is not using a callback function, proper way to access current ...area should be this:

onChange={e => setArea((currentArea) => [...currentArea, { [lang]: e.target.value }]}

This way you always have the most up-to-date version of area.

Now, to update the specific object by the index, you would do the following:

onChange={e => setArea((currentArea) => {
  const { value } = e.target;
  
  const newArea = currentArea.map((singleArea, areaIndex) => {
    if (areaIndex === index) {
      return { [lang]: value }
    }
 
    return singleArea;
  });

  return newArea;
}}

2. Lacks the "de" language

Again, you're explicitly only adding one language to it :)

{ [lang]: value } // { en: 'something' }

Not sure how to fix this one for you, but at least you can understand why its wrong, It should be something like (just a concept, this won't work):

{ [langDe]: value, [langEn]: value } // { de: 'Guten Tag', en: 'something' }
🌐
Upmostly
upmostly.com › home › tutorials › how to update state onchange in an array of objects using react hooks
How To Update State onChange in an Array of Objects using React Hooks - Upmostly
September 17, 2022 - Set the onChange prop to the updateState function and pass the index as a parameter. // App.js import { useState } from 'react'; import './App.css'; function App() { const datas = [ { id: 1, name: 'Nick', age: 21 }, { id: 2, name: 'Lara', age: ...
🌐
Medium
medium.com › @pradipdev › how-to-update-array-object-in-usestate-react-hook-78839ee86cdd
How to Update Array Object in UseState (React Hook) - pradip - Medium
December 7, 2023 - const [your_arr_obj, setYour_arr_obj] = useState([ { name: ‘rina’, active: true}, { name: ‘rino’, active: false}, ]) 2. Create method to handle when user click the component · const handleClick = (index) => { // you have to create new arr and assign with ‘your_arr_obj’ ·
🌐
Medium
medium.com › @hongsi140626 › programming-all-about-state-update-in-react-object-array-30e33bb89d38
All about state update in React(Object & Array) | by Jake Hong | Medium
April 17, 2023 - Update objects in array state · const [blogs, setBlogs] = useState([ { id: 1, title: 'A_Blog', content: 'A_Content', }, { id: 2, title: 'B_Blog', content: 'B_Content', }, ]); const tempBlogs = [...blogs]; const index = tempBlogs.findIndex((tempBlog) => tempBlog.id === 2); tempBlogs[index]['content'] = 'New_Content'; setBlogs(tempBlogs); This works properly, however, the tempBlogs array itself is new, the items themselves are the same as in the original blogs array (shallow copy by spread operator).
Find elsewhere
🌐
Delft Stack
delftstack.com › home › howto › react › react usestate array
How to Update Array Values in React useState Hook | Delft Stack
February 2, 2024 - Now, we will move to the index.js file of our project folder and write these codes: ... import React, { useState, useEffect } from "react"; import ReactDOM from "react-dom"; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; const StateSelector = () => { const initialValue = [{ id: 0, value: " --- Select a State ---" }]; const allowedState = [ { id: 1, value: "Alabama" }, { id: 2, value: "Georgia" }, { id: 3, value: "Tennessee" } ]; const [stateOptions, setStateValues] = useState(initialValue); console.log(initialValue.length); useEffect(() => { set
🌐
Bobby Hadz
bobbyhadz.com › blog › react-update-object-in-array
How to Update an Array of Objects state in React | bobbyhadz
April 6, 2024 - Use the map() method to iterate over the array. On each iteration, check if a certain condition is met. Update the object that satisfies the condition and return all other objects as is.
🌐
DEV Community
dev.to › shareef › how-to-work-with-arrays-in-reactjs-usestate-4cmi
How to work with Arrays in ReactJS useState. - DEV Community
May 21, 2021 - ReactJS introduce Hooks in React 16.8. And since then the most used hook is "useState" || "useEffect" In this blog, We will take a look at how work with Arrays and "useState" hook. ... const friendsArray = [ { name: "John", age: 19, }, { name: "Candy", age: 18, }, { name: "mandy", age: 20, }, ]; ... import { useState } from "react"; const App = () => { const [friends, setFriends] = useState(friendsArray); // Setting default value const handleAddFriend = () => { ... }; return ( <main> <ul> // Mapping over array of friends {friends.map((friend, index) => ( // Setting "index" as key because name and age can be repeated, It will be better if you assign uniqe id as key <li key={index}> <span>name: {friend.name}</span>{" "} <span>age: {friend.age}</span> </li> ))} <button onClick={handleAddFriend}>Add Friends</button> </ul> </main> ); }; export default App;
🌐
DEV Community
dev.to › joelynn › react-hooks-working-with-state-arrays-2n2g
React hooks - working with state (array of objects) - DEV Community
August 6, 2021 - // delcare the function function handleAddNewUser() { // it's important to not mutate state directly, so here we are creating a copy of the current state using the spread syntax const updateUsers = [ // copy the current users state ...users, // now you can add a new object to add to the array { // using the length of the array for a unique id id: users.length + 1, // adding a new user name name: "Steve", // with a type of member type: "member" } ]; // update the state to the updatedUsers setUsers(updateUsers); } Click the "Add user" button and you will see a new list item added to the state: // boolean state to know if we are editing (this will let us display const [isEditing, setIsEditing] = useState(false); // object state to set so we know which todo item we are editing const [currentUser, setCurrentUser] = useState({});
🌐
DhiWise
dhiwise.com › post › react-update-array-of-objects-in-state-a-developer-guide
Guide to React Update Array of Objects in State
June 7, 2024 - The state should be immutable, meaning you should never modify it directly. Instead, you should create a new state with the desired changes and use the setState function or the useState hook to update the state.
🌐
Medium
medium.com › @highlanderfullstack › how-to-work-with-arrays-in-reactjs-usestate-b3b3f4d66d43
How to work with Arrays in ReactJS useState. | by Desenvolvedor Full Stack | Medium
March 28, 2023 - ReactJS introduce Hooks in React 16.8. And since then the most used hook is “useState” || “useEffect” In this blog, We will take a look at how work with Arrays and “useState” hook. ... const friendsArray = [ { name: "John", age: 19, }, { name: "Candy", age: 18, }, { name: "mandy", age: 20, }, ]; ... const App = () => { const [friends, setFriends] = useState(friendsArray); // Setting default value const handleAddFriend = () => { ... }; return ( <main> <ul> // Mapping over array of friends {friends.map((friend, index) => ( // Setting "index" as key because name and age can be repeated, It will be better if you assign uniqe id as key <li key={index}> <span>name: {friend.name}</span>{" "} <span>age: {friend.age}</span> </li> ))} <button onClick={handleAddFriend}>Add Friends</button> </ul> </main> ); };export default App;
🌐
Thesshguy
blog.thesshguy.com › react-usestate-arrays
Managing Arrays with React's useState Hook - Blog - Sai Hari
July 25, 2024 - Technically the Mutate Button does update the value, React just doesn't know about it until the Update Button is clicked and setList is called with a new array reference. ... Instead of mutating the existing array, use it as the starting point to create a new array. ... function ListOfNumbers() { const [list, setList] = useState([1]); return ( <div> {/** * It's not a good practice to use the index * as the key, but hey this is just an example.
🌐
Logfetch
logfetch.com › js-update-object-in-state-array-by-index
How to Update an Object in useState Array By Index in JavaScript - LogFetch
January 26, 2022 - This means that slice(1,3) is obtaining the array over indexes [1,3). Finally, we can update an object in a state array using this slice() function as well as the spread operator.
Top answer
1 of 1
7

Mutation You are mutating the existing state when you do

let newArr = [...rowDataTracker];
// ..
  newArr[itemIndex]["payload"][columnId] = newValue;

because spread syntax only creates a shallow copy of the array or object. This should never be done in React. (See end of answer for how to fix it - create the new obj no matter what, then either push it to the new array, or replace the existing object in the array, without mutating the existing object in the array)

Dot notation Most people prefer to read and write using dot notation in JS when possible - it's more concise and easier to read. ESLint rule: dot-notation. You can use .payload instead of ["payload"].

Property/variable naming The rowId refers to the same thing as the itemNbr, which looks to refer to the same thing as the id in the rowDataTracker array. It's good to be consistent with variable names and properties when possible - consider picking one and sticking with it. If you use the variable name itemNbr, you can use it shorthand in the object literal:

const obj = {
  itemNbr,
  // ...

id or itemNbr or both? You check against the id property in rowDataTracker in ifItemExist, but you push to the same array with a property named itemNbr. This looks very likely to be a typo. You probably meant to push an object with an id property, or you wanted to check against the itemNbr property instead.

Misleading function name ifItemExist doesn't return a boolean indicating if an item exists - it returns the index of the possibly-existing item. Maybe call it getItemIndex.

Prefer const over let when the variable name doesn't need to be reassigned.

In all:

const getItemIndex = (arr, item) => {
  return arr.findIndex((e) => e.id === item);
};

function storeEdit(id, field, newValue) {
  const itemIndex = getItemIndex(rowDataTracker, id);
  const obj = {
    id,
    payload: {
      ...rowDataTracker[itemIndex]?.payload,
      [field]: newValue
    }
  };
  if (itemIndex === -1) {
    setRowDataTracker([...rowDataTracker, obj]);
    return;
  }
  const newArr = [...rowDataTracker];
  newArr[itemIndex] = obj;
  setRowDataTracker(newArr);
}