Whats the Best Way to Update an Object in an Array in Reactjs

Whats the best way to update an object in an array in ReactJS?

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?

How to update a single key value pair in an array of objects in react?

You can use the map function to produce a new array to set the state with.

const updateAge = (id, age) => {
console.log(id, age);
setList(
list.map((item) => {
if (item.id === id) {
return { ...item, age };
} else {
return item;
}
})
);
};

If the id matches, it will merge the new age, otherwise it will return the item unchanged.

Note that it will only produce new objects for updated age, the rest will be the same references. That is to say it will be a new array, but most objects (except the changed one) will be the same. If you want new objects you can return { ...item } in the else clause as well.

In this particular case React will only be concerned that you set the state with new array.

Here's the sandbox

React updating single object of an array of objects - map vs entire array

Option 3 and opntion 4 are incorrect, the prevState/myState is an array and you are returning an object, this will surely cause error in typescript. As to option 1 and 2, they only differ between declaritive and imperitive way of programming. And declaritive programming makes more readable code.

On option 1:

const newState = [...myState];
newState[index].description = "new desc";
// Access index position as opposed to id
// since the item's position may not align with it's id
setState(newState );

On option 2 with map function, it can be written as follows:

setState(myState.map(item => item.id === id ? {...item, item.description: "new desc"} : item))

In conclusion, use map/filter is more preferable to update array.

Here's sandbox to compare option 1 and 2

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.

How to update an object in an array of Objects using setState

You can get do a cloning of the state object using spread operator and then find the index of object in array with a given id using findIndex method Modify the object and set the state.

constructor(props) {
super(props);
this.state = {
data: [{
id: 0,
title: 'Buy a',
status: 0, // 0 = todo, 1 = done
},
{
id: 1,
title: 'Buy b',
status: 0,
},
{
id: 2,
title: 'Buy c',
status: 0,
}
]
};
this.onTitleChange = this.onTitleChange.bind(this);
}
onTitleChange(id, title) {
var data = [...this.state.data];
var index = data.findIndex(obj => obj.id === id);
data[index].title = title;
this.setState({data});
}

trying to update an existing object in array of objects using react

Instead of using .find, use .findIndex first, so that you can replace the object in the array with the new object. (Remember not to use Object.assign in React when the first parameter is stateful, because that'd result in a state mutation)

You should also check that all of these objects exist first before trying to update.

if (!formValues.constructionSet || !values || !values.opaqueMaterial) {
return;
}
const newMat = values.opaqueMaterial;
const index = addedOpaqueMaterials.findIndex(mat => mat.id === newMat.id);
if (index === -1) {
// Add to the end of the existing array:
const newOpaqueMaterials = [
...addedOpaqueMaterials,
newMat
];
// put newOpaqueMaterials into state
} else {
// Replace the object in the state array with the new object:
const newOpaqueMaterials = [
...addedOpaqueMaterials.slice(0, index),
newMat,
...addedOpaqueMaterials.slice(index + 1)
];
// put newOpaqueMaterials into state
}

Correct modification of state arrays in React.js

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]
})

What is the proper way to update multiple objects in an array of objects in react?

You could let each object have its own component and then pass the object properties in the components and then use React.memo() so that it would only cause a render if it detects a change.

By default it will only shallowly compare the objects in the props which should be enough for you. But if its more complex, then you can also write a custom function.

As per docs

function MyComponent(props) {
/* render using props */
}
function areEqual(prevProps, nextProps) {
/*
return true if passing nextProps to render would return
the same result as passing prevProps to render,
otherwise return false
*/
}
export default React.memo(MyComponent, areEqual);


Related Topics



Leave a reply



Submit