How to Update State.Item[1] in State Using Setstate

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



Leave a reply



Submit