How can I update state.item[1] in state using setState?
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
How to update state with usestate in an array of objects?
You can safely use javascript's array map functionality since that will not modify existing state, which react does not like, and it returns a new array. The process is to loop over the state's array and find the correct id. Update the done
boolean. Then set state with the updated list.
const toggleDone = (id) => {
console.log(id);
// loop over the todos list and find the provided id.
let updatedList = state.todos.map(item =>
{
if (item.id == id){
return {...item, done: !item.done}; //gets everything that was already in item, and updates "done"
}
return item; // else return unmodified item
});
setState({todos: updatedList}); // set state to new object with updated list
}
Edit: updated the code to toggle item.done
instead of setting it to true.
Updating a specific field of object state in React
What you are doing is correct.
That's the proper way for updating the state based on the current value of the state. It's called the functional form of the setState
call.
setState((prevState) => {
// Do something with prevState
return newState; // Be aware to not mutate the prevState directly
});
Examples:
// Array
setState((prevState) => {
const newState = Array.from(prevState);
newState.push('foo');
return newState;
});
// Object
setState((prevState) => {
return({
...prevState,
foo: 'bar'
});
});
The main thing is that the object/array reference needs to change!
How to edit an item in a state array?
My suggestion is to get used to use immutable operations, so you don't modify internal state object.
As pointed in the react docs:
Never mutate this.state directly, as calling setState() afterwards may
replace the mutation you made. Treat this.state as if it were
immutable.
In this case, you can [1] use slice()
to get a new copy of the Array, [2] manipulate the copy, and, then, [3] setState with the new Array. It's a good practice.
Something like that:
const newIds = this.state.ids.slice() //copy the array
newIds[1] = 'B' //execute the manipulations
this.setState({ids: newIds}) //set the new state
Updating an object with setState in React
There are multiple ways of doing this, since state update is a async operation, so to update the state object, we need to use updater function with setState
.
1- Simplest one:
First create a copy of jasper
then do the changes in that:
this.setState(prevState => {
let jasper = Object.assign({}, prevState.jasper); // creating copy of state variable jasper
jasper.name = 'someothername'; // update the name property, assign a new value
return { jasper }; // return new object jasper object
})
Instead of using Object.assign
we can also write it like this:
let jasper = { ...prevState.jasper };
2- Using spread syntax:
this.setState(prevState => ({
jasper: { // object that we want to update
...prevState.jasper, // keep all other key-value pairs
name: 'something' // update the value of specific key
}
}))
Note: Object.assign
and Spread Operator
creates only shallow copy, so if you have defined nested object or array of objects, you need a different approach.
Updating nested state object:
Assume you have defined state as:
this.state = {
food: {
sandwich: {
capsicum: true,
crackers: true,
mayonnaise: true
},
pizza: {
jalapeno: true,
extraCheese: false
}
}
}
To update extraCheese of pizza object:
this.setState(prevState => ({
food: {
...prevState.food, // copy all other key-value pairs of food object
pizza: { // specific object of food object
...prevState.food.pizza, // copy all pizza key-value pairs
extraCheese: true // update value of specific key
}
}
}))
Updating array of objects:
Lets assume you have a todo app, and you are managing the data in this form:
this.state = {
todoItems: [
{
name: 'Learn React Basics',
status: 'pending'
}, {
name: 'Check Codebase',
status: 'pending'
}
]
}
To update the status of any todo object, run a map on the array and check for some unique value of each object, in case of condition=true
, return the new object with updated value, else same object.
let key = 2;
this.setState(prevState => ({
todoItems: prevState.todoItems.map(
el => el.key === key? { ...el, status: 'done' }: el
)
}))
Suggestion: If object doesn't have a unique value, then use array index.
React state updating without setstate, takes on state of deleted item (SOLVED)
This is because you are using the index as key.
Because of that when you delete an element you call the Array.filter
then you the elements can change the index of the array which when React tries to rerender the notes and as the index changes it cannot identify the note you've deleted.
Try using a unique id (e.g. an id from the database or UUID) as a key instead.
I hope it solves your problem!
React State update a nested array with objects based on the id when iterated
You need to pass additional parameters to your handleChange
as item ID which you want to update and property name because without these you will not be able to identify which property to update dynamically. This way you can use the same handleChange
for multiple inputs.
See below code -
function handleChange(e, itemId, property) {
const value = e.target.value;
//copying data to temp variable so that we do not directly mutate original state
const tempWorkingHours = [...data.working_hours];
//findIndex to find location of item we need to update
let index = tempWorkingHours.findIndex(item => item.id == itemId);
// -1 check to see if we found that object in working hours
if(index != -1){
tempWorkingHours[index] = {
...tempWorkingHours[index], //keeping existing values in object
[property]: value //here property can be "price" or "description"
}
}
setData({ ...data, working_hours: tempWorkingHours })
}
{
data.working_hours.map(item =>
<>
<input type={text} value={item.description}
onChange={(e) => handleChange(e, item.id, "description")} />
<input type={text} value={item.price}
onChange={(e) => handleChange(e, item.id, "price")} />
</>
)
}
How to conditionally update state based on previous state in react functional component?
You are almost there.
const [value, setValue] = useState({})
setValue((prevState) => {
if (condition to not update) {
return prevState;
} else {
return newState;
}
});
You don't necessarily need to do it from the setValue
call. You can also do it like this:
const updateSomething = () => {
if (shouldUpdate)
setValue(newState);
}
Related Topics
What Are the Differences Between Deferred, Promise and Future in JavaScript
How to Decode Jwt Token in JavaScript Without Using a Library
Controlling Fps with Requestanimationframe
Difference Between Textcontent VS Innertext
React-Router - Pass Props to Handler Component
How to Make Exe Files from a Node.Js App
How to Call Reduce on an Array of Objects to Sum Their Properties
How to Tell If Caps Lock Is on Using JavaScript
How to Implement Authenticated Routes in React Router 4
Get Hours Difference Between Two Dates in Moment Js
Remove All Elements Contained in Another Array
How to Turn JavaScript Array into Comma-Separated List
What Does the Plus Sign Do in '+New Date'
Add a "Hook" to All Ajax Requests on a Page
How to Sort an Array of Objects with Jquery or JavaScript