2025
Here's an update showing a live code demo so you can verify the results in your own browser -
function App(props) {
const [state, setState] = React.useState(props.initialState)
function updateObj(index, key, value) {
setState(s => [
...s.slice(0, index),
{ ...s[index], [key]: value },
...s.slice(index + 1),
])
}
return <div>
{state.map((o, index) =>
<div key={o.id}>
<input value={o.foo} onChange={e => updateObj(index, "foo", e.target.value)} />
<input value={o.bar} onChange={e => updateObj(index, "bar", e.target.value)} />
<input value={o.qux} onChange={e => updateObj(index, "qux", e.target.value)} />
</div>
)}
<pre>{JSON.stringify(state, null, 2)}</pre>
</div>
}
ReactDOM.createRoot(document.querySelector("#app")).render(
<App
initialState={[
{ id: 1, foo: "a", bar: "b", qux: "c" },
{ id: 2, foo: "j", bar: "k", qux: "l" },
{ id: 3, foo: "x", bar: "y", qux: "z" },
]}
/>
)
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>
2020
Things have changed a lot since I wrote this, but leaving the original for posterity.
Your update function would look like this -
updateItem(id, itemAttributes) {
var index = this.state.items.findIndex(x=> x.id === id);
if (index === -1)
// handle error
else
this.setState({
items: [
...this.state.items.slice(0,index),
Object.assign({}, this.state.items[index], itemAttributes),
...this.state.items.slice(index+1)
]
});
}
And you use it like this -
this.updateItem(2, {someattr: 'a new value'});
Gross right?
You're going to have a big headache in general if you continue to build a complex application in this manner. I would recommend you look into redux or some other Flux implementation that is better suited for solving these problems.
Redux uses a concept of state reducers which each work on a specific slice of the state of your application. That way you don't have to manually dig through your entire state each time you want to affect a deep change.
The creator of Redux, Dan Abramov, has made two video courses available online for free. Dan is an excellent teacher and I felt comfortable with the Redux pattern after spending just one afternoon with it.
- https://egghead.io/courses/getting-started-with-redux
- https://egghead.io/courses/building-react-applications-with-idiomatic-redux
2025
Here's an update showing a live code demo so you can verify the results in your own browser -
function App(props) {
const [state, setState] = React.useState(props.initialState)
function updateObj(index, key, value) {
setState(s => [
...s.slice(0, index),
{ ...s[index], [key]: value },
...s.slice(index + 1),
])
}
return <div>
{state.map((o, index) =>
<div key={o.id}>
<input value={o.foo} onChange={e => updateObj(index, "foo", e.target.value)} />
<input value={o.bar} onChange={e => updateObj(index, "bar", e.target.value)} />
<input value={o.qux} onChange={e => updateObj(index, "qux", e.target.value)} />
</div>
)}
<pre>{JSON.stringify(state, null, 2)}</pre>
</div>
}
ReactDOM.createRoot(document.querySelector("#app")).render(
<App
initialState={[
{ id: 1, foo: "a", bar: "b", qux: "c" },
{ id: 2, foo: "j", bar: "k", qux: "l" },
{ id: 3, foo: "x", bar: "y", qux: "z" },
]}
/>
)
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>
2020
Things have changed a lot since I wrote this, but leaving the original for posterity.
Your update function would look like this -
updateItem(id, itemAttributes) {
var index = this.state.items.findIndex(x=> x.id === id);
if (index === -1)
// handle error
else
this.setState({
items: [
...this.state.items.slice(0,index),
Object.assign({}, this.state.items[index], itemAttributes),
...this.state.items.slice(index+1)
]
});
}
And you use it like this -
this.updateItem(2, {someattr: 'a new value'});
Gross right?
You're going to have a big headache in general if you continue to build a complex application in this manner. I would recommend you look into redux or some other Flux implementation that is better suited for solving these problems.
Redux uses a concept of state reducers which each work on a specific slice of the state of your application. That way you don't have to manually dig through your entire state each time you want to affect a deep change.
The creator of Redux, Dan Abramov, has made two video courses available online for free. Dan is an excellent teacher and I felt comfortable with the Redux pattern after spending just one afternoon with it.
- https://egghead.io/courses/getting-started-with-redux
- https://egghead.io/courses/building-react-applications-with-idiomatic-redux
If you were using functional components and the useState hook, you could easily use map, as long as you don't mind substituting the entire object
const [items, setItems] = useState ([
{id: 1, someattr: "a string", anotherattr: ""},
{id: 2, someattr: "another string", anotherattr: ""},
{id: 3, someattr: "a string", anotherattr: ""},
])
setItems (
items.map((item) => {
return item.id === updatedItem.id? updatedItem: item;
})
);
How can I update state.item[1] in state using setState?
How do you update a value inside an array of objects with useState?
Whats the best way to update an object in an array in ReactJS?
Is it possible to update a state array and keep the reference to an element on it?
How do you update an array of objects in React state?
How do you update nested array of objects in React state?
How do you update an array of objects with another array of objects?
Videos
Here's how you can do it without helper libs:
handleChange: function (e) {
// 1. Make a shallow copy of the items
let items = [...this.state.items];
// 2. Make a shallow copy of the item you want to mutate
let item = {...items[1]};
// 3. Replace the property you're intested in
item.name = 'newName';
// 4. Put it back into our array. N.B. we *are* mutating the array here,
// but that's why we made a copy first
items[1] = item;
// 5. Set the state to our new copy
this.setState({items});
},
You can combine steps 2 and 3 if you want:
let item = {
...items[1],
name: 'newName'
}
Or you can do the whole thing in one line:
this.setState(({items}) => ({
items: [
...items.slice(0,1),
{
...items[1],
name: 'newName',
},
...items.slice(2)
]
}));
Note: I made items an array. OP used an object. However, the concepts are the same.
You can see what's going on in your terminal/console:
โฏ node
> items = [{name:'foo'},{name:'bar'},{name:'baz'}]
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ]
> clone = [...items]
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ]
> item1 = {...clone[1]}
{ name: 'bar' }
> item1.name = 'bacon'
'bacon'
> clone[1] = item1
{ name: 'bacon' }
> clone
[ { name: 'foo' }, { name: 'bacon' }, { name: 'baz' } ]
> items
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ] // good! we didn't mutate `items`
> items === clone
false // these are different objects
> items[0] === clone[0]
true // we don't need to clone items 0 and 2 because we're not mutating them (efficiency gains!)
> items[1] === clone[1]
false // this guy we copied
You could use the update immutability helper for this:
this.setState({
items: update(this.state.items, {1: {name: {$set: 'updated field name'}}})
})
Or if you don't care about being able to detect changes to this item in a shouldComponentUpdate() lifecycle method using ===, you could edit the state directly and force the component to re-render - this is effectively the same as @limelights' answer, as it's pulling an object out of state and editing it.
this.state.items[1].name = 'updated field name'
this.forceUpdate()
Post-edit addition:
Check out the Simple Component Communication lesson from react-training for an example of how to pass a callback function from a state-holding parent to a child component which needs to trigger a state change.
Hi!
I have an array of objects in useState:
const [items, setItems] = useState([
{id: 0, data: "a"}, {id: 1, data: "b"}, {id: 2, data: "d"} ])How can I search an object by id and update a value? For example, if I wanted to change the data value to "c" in object with id 2?
Thanks so much!
I quite like doing this with Object.assign rather than the immutability helpers.
handleCommentEdit: function(id, text) {
this.setState({
data: this.state.data.map(el => (el.id === id ? Object.assign({}, el, { text }) : el))
});
}
I just think this is much more succinct than splice and doesn't require knowing an index or explicitly handling the not found case.
If you are feeling all ES2018, you can also do this with spread instead of Object.assign
this.setState({
data: this.state.data.map(el => (el.id === id ? {...el, text} : el))
});
While updating state the key part is to treat it as if it is immutable. Any solution would work fine if you can guarantee it.
Here is my solution using immutability-helper:
jsFiddle:
var update = require('immutability-helper');
handleCommentEdit: function(id, text) {
var data = this.state.data;
var commentIndex = data.findIndex(function(c) {
return c.id == id;
});
var updatedComment = update(data[commentIndex], {text: {$set: text}});
var newData = update(data, {
$splice: [[commentIndex, 1, updatedComment]]
});
this.setState({data: newData});
},
Following questions about state arrays may also help:
- Correct modification of state arrays in ReactJS
- what is the preferred way to mutate a React state?
So I know in React, when you have an array as a state, and you want to add/remove/update one of its elements, you have to create an entirely new array.
Cool, but what if I have an array of objects, one of which I'd like to keep an (updated) reference to? For example:
[arr, setArr] = useState().. [ref, setRef] = useState()... setRef(arr[5]) setArr(...) // set a new array equal to the old one, except arr[5] changes one property // ref variable now still points to my old arr[5], not the new one. // should I setRef(arr.find(f...)) to find my object again based on information I know is immutable? Would that work with the async nature of setStates?
Is there a way to accomplish this? If not, what should I do instead?
I just want to mutate a property in one object of my array and still keep a reference to that object.
clickedSquare.on = !clickedSquare.on; is a state mutation. Don't mutate React state.
The reason the following code is likely working is because it has shallow copied the squares state array which triggers a rerender and exposes the mutated array elements.
function toggle(clickedSquare) {
clickedSquare.on = !clickedSquare.on; // <-- mutation!
setSquares(prevSquares => [...prevSquares]); // new array for Reconciliation
}
It may not have any adverse effects in this specific scenario, but mutating state is a good foot gun and likely to cause potentially difficult bugs to debug/diagnose, especially if their effects aren't seen until several children deeper in the ReactTree.
Just always use the first method and apply the Immutable Update pattern. When updating any part of React state, even nested state, new array and object references need to be created for React's Reconciliation process to work correctly.
function toggle(clickedSquare) {
setSquares(prevSquares => prevSquares.map((square) => // <-- new array
square === clickedSquare
? { ...square, on: !square.on } // <-- new object
: square
));
}
Here, You are using map() method which return only true or false for that condition. So, You should use filter() method instead of map() method which return filtered data for that condition.
For Example:
const arr = [
{
name: 'yes',
age: 45
},
{
nmae: 'no',
age: 15
}
]
const filterByMap = arr.map(elm => elm.age > 18)
console.log(filterByMap) // outputs --> [ true, false ]
const filterByFilter = arr.filter(elm => elm.age > 18)
console.log(filterByFilter) // outputs --> [ { name: 'yes', age: 45 } ]