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 }];
};
I want to make a swipable card components with user inputs. I have an array of objects with keys predefined so that I can capture the user input through fields. Say, for example, if I want to have 4 slides in that card with user inputs, how to handle the state in such a situation? how to correctly change the values of a specific item of the object without mutating other objects?
const object = {title: '', name: '', age: '', pic: ''};
const [card, setCard] = useState([object]);This is how I defined the state.
I want to change the values of an object in the array by using the e.target.name and e.target.value. How to do it for 4 slides without affecting one another?