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;
})
);
reactjs - SetState of an array of Objects in React - Stack Overflow
Efficient way to add objects to state arrays?
How to set state to an array of objects in react
How to change property of one object in 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?
How do I update a state object in Reactjs?
Videos
answered March 25 2018
This is how you would use setState and prevstate to update a certain attribute of an object in your data structure.
this.setState(prevState => ({
itemList: prevState.itemList.map(
obj => (obj._id === 1234 ? Object.assign(obj, { description: "New Description" }) : obj)
)
}));
answered Dec 12 2019 (REACT HOOKS)
import React, { useState } from 'react';
const App = () => {
const [data, setData] = useState([
{
username: '141451',
password: 'password',
favoriteFood: 'pizza',
},
{
username: '15151',
password: '91jf7jn38f8jn3',
favoriteFood: 'beans'
}
]);
return (
<div>
{data.map(user => {
return (
<div onClick={() => {
setData([...data].map(object => {
if(object.username === user.username) {
return {
...object,
favoriteFood: 'Potatos',
someNewRandomAttribute: 'X'
}
}
else return object;
}))
}}>
{JSON.stringify(user) + '\n'}
</div>
)
})}
</div>
)
}
to update state constructed like this you will have to find index of element you want to update, copy the array and change found index.
it's easier and more readable if you keep list of records as object, with id as a key and record as a value.
A bit new to reactjs. I realized every time i want to add something in an array that order matters i have to write something like this:
setState([...prev, newItemObj])
however, this is basically bigO(n). Wondering if theres a better way to do this if the array is very big.
I read that react only copies object references, not deep copies. Does that mean its basically O(1)?
Here you go :
You can pass event and index both to the function :
Ref is taken from your prev question :
<input type="text" name={i.term} placeholder="TERM"
value={this.state.allTerms[i].term}
onChange={(e) =>this.onChange(e,i)}> // <--- Change
</input>
Update something like this :
onChangeTerm = (event , index) => {
this.setState({
allTerms : [
...this.state.allTerms.splice(0,index) ,
{
...this.state.allTerms[index],
term: event.target.value
},
...this.state.allTerms.splice(index+1) ,
});
}
Its bad idea to have your state updated inside a loop, I would suggest you use object spread operator and once you finished building the state object, you assign it to your component state:
onChangeTerm = (event) => {
let allTerms = {}
for(var i = 0; i < this.props.numberOfTerms.length; i++) {
allTerms = { ...this.state.allTerms[i], term: event.target.value }
}
this.setState({ allTerms });
}
Your example is in fact mutating state here:
flashcard.show = !flashcard.show;
At this point, flashcard refers directly to an object in state, so altering one of its properties is a mutation.
You need a way to identify the objects in state so that you can extract one, clone it individually, and then insert it back into a cloned state array in its original position. Without changing any of your data, you could do this by passing the array position of the flashcard when you call toggleFlashcard.
{flashcards.map((flashcard: IFlashcard, i: number) => {
return (
<>
<li><span onClick={() => toggleFlashcard(i)}>{flashcard.noun}</span>
{flashcard.show && (
<>
: {flashcard.article}
</>
)}
</li>
</>
)
})}
Now the toggleFlashcard event handler should look something like this:
const toggleFlashcard = (i: number) => {
const clonedCard = {...flashcards[i]};
clonedCard.show = !clonedCard.show;
const clonedState = [...flashcards];
clonedState[i] = clonedCard;
setFlashcards(clonedState);
}
If you don't want to mutate anything, please try this solution.
const toggleFlashcard = (flashcard: IFlashcard) => {
const flashcardIndex = flashcards.findIndex(f => f === flashcard);
const newFlashcards = [...flashcards];
newFlashcards[flashcardIndex]= { ...flashcard, show: !flashcard.show };
setFlashcards(newFlashcards);
};
And this is not related to the main topic but the key attribute is missing here.
{flashcards.map((flashcard: IFlashcard, index: number) => {
...
<li key={index}><span onClick={() => toggleFlashcard(flashcard)}>{flashcard.noun}</span>
If you don't specify the key attribute, you will see React warnings.
Assuming that response can be added directly to result array, you can:
setResult(result => [...result, response]);
This will append the new response from the previous result state and by using array spread operator.
You could just pass the entire array into the hook and return the result as an array. You should also use useEffect for async logic. I rewrote your code to process all 3 fields at once:
const useCableMatch = (searchInput) => {
const [result, setResult] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const client = useApolloClient();
useEffect((searchInput) => {
setIsError(false);
setIsLoading(true);
let response
for(let field of searchInput){
try {
const { data } = await client.query({
query: GET_VALID_CABLE,
variables: { pn: `%${field}%` },
});
response = { ...data };
} catch (error) {
setIsError(true);
}
}
setResult(current => {query, ...current, ...response);
setIsLoading(false);
};
}, [searchInput])
return [{ result, isLoading, isError }];
};
The React docs says:
Treat this.state as if it were immutable.
Your push will mutate the state directly and that could potentially lead to error prone code, even if you are "resetting" the state again afterwards. For example, it could lead to that some lifecycle methods like componentDidUpdate won’t trigger.
The recommended approach in later React versions is to use an updater function when modifying states to prevent race conditions:
this.setState(prevState => ({
arrayvar: [...prevState.arrayvar, newelement]
}))
The memory "waste" is not an issue compared to the errors you might face using non-standard state modifications.
Alternative syntax for earlier React versions
You can use concat to get a clean syntax since it returns a new array:
this.setState({
arrayvar: this.state.arrayvar.concat([newelement])
})
In ES6 you can use the Spread Operator:
this.setState({
arrayvar: [...this.state.arrayvar, newelement]
})
Easiest, if you are using ES6.
initialArray = [1, 2, 3];
newArray = [...initialArray, 4]; // --> [1,2,3,4]
New array will be [1,2,3,4]
to update your state in React
this.setState({
arrayvar: [...this.state.arrayvar, newelement]
});
Learn more about array destructuring