Ever tried to update an array in the React and ended up mutating the existing state?
Then you might be wondering why your component didn't update. Well you are not alone i did that too, and turns out a lot of other people face the exact same issue.
Let's learn the correct way to do it today...
Let's take an example of a list.
- We will allow addition of a new items to the list.
- Deletion of an item.
- Adding an item at a specific point in an array.
Addition of an element.
// Method 1 -> Use array destructure
const addUser = () => {
const newUser = {
id: Date.now(),
username: `User ${users.length + 1}`
};
const newUsers = [...users, newUser];
setUsers(newUsers);
};
// Method 2 -> Use slice method with combination of push method.
const addUser = () => {
const newUser = {
id: Date.now(),
username: `User ${users.length + 1}`
};
const newUsers = users.slice();
newUsers.push(newUser);
setUsers(newUsers);
};
With method one we are simply using ES6 array destructing and appending an element to it. Where as method 2 goes old school using slice
and push
methods.
Deletion of an element.
// Method 1 -> Use array destructure
const removeUser = (index) => () => {
const newUsers = [...users];
newUsers.splice(index, 1);
setUsers(newUsers);
};
// Method 2 -> Use slice method.
const removeUser = (index) => () => {
const newUsers = users.slice();
newUsers.splice(index, 1);
setUsers(newUsers);
};
With method one we are simply using ES6 array destructing to shallow clone the array and then mutating the new array by deleting the element. With method 2 we are using slice
to shallow clone the array.
Addition of an element at a specific index.
// Method 1 -> Use array destrcture.
const addAfter = (index) => () => {
const newUser = {
id: Date.now(),
username: `User ${users.length + 1}`
};
const newUsers = [
...users.slice(0, index + 1),
newUser,
...users.slice(index + 1)
];
setUsers(newUsers);
};
// Method 2 -> Using splice
const addAfter = (index) => () => {
const newUser = {
id: Date.now(),
username: `User ${users.length + 1}`
};
const newUsers = [...users];
newUsers.splice(index + 1, 0, newUser)
setUsers(newUsers);
};
With method one we are using slice
, slice(start, length)
we grab all the elements till the given index. After that we append the new element, lastly using slice(index + 1)
we are taking the remaining items from the original array and concentrate everything using ES6 array destructing.
Method 2 we are taking advantage of a quirk that comes with array [splice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice)
with splice we can push the elements in array as well using its 3rd argument.
Full Code [https://codesandbox.io/s/objective-rgb-4z4yz?file=/src/App.js]
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [users, setUsers] = useState([
{ id: Date.now() + 1, username: "User 1" },
{ id: Date.now() + 2, username: "User 2" }
]);
const addUser = () => {
const newUser = {
id: Date.now(),
username: `User ${users.length + 1}`
};
// Method 1 -> Use array destructure
const newUsers = [...users, newUser];
// Method 2 -> Use slice method with combination of push method.
// const newUsers = users.slice();
// newUsers.push(newUser);
setUsers(newUsers);
};
const removeUser = (index) => () => {
// Method 1 -> Use array destrcture.
const newUsers = [...users];
newUsers.splice(index, 1);
// Method 2 -> Use slice method.
// const newUsers = users.slice();
// newUsers.splice(index, 1);
setUsers(newUsers);
};
const addAfter = (index) => () => {
const newUser = {
id: Date.now(),
username: `User ${users.length + 1}`
};
// Method 1 -> Use array destrcture.
// const newUsers = [
// ...users.slice(0, index + 1),
// newUser,
// ...users.slice(index + 1)
// ];
// Method 2 -> Using splice
const newUsers = [...users];
newUsers.splice(index + 1, 0, newUser)
setUsers(newUsers);
};
return (
<>
<button onClick={addUser}>Add User</button>
{users.map((user, index) => (
<div className="user" key={user.id}>
{user.username}
<button onClick={removeUser(index)}>Remove User</button>
<button onClick={addAfter(index)}>Add immediate next User</button>
</div>
))}
</>
);
}
Top comments (7)
You can use map and Object.assign too!
Can you put more context here?,
if we are talking about Map data type, then that's a different process to handle it.
If it's about the map function in the Array prototype, i agree we get a new array in return but that is a relatively expensive operation.
For object assign, that's of objects only right?, We can apply them for arrays. I'm planning a follow up article for object state change as well.
map function of array is a simple way to exclude an element from array and make a new array.
You can use Object.assign to make new array. I do this hack:
clonedArray = Object.assign([], toBeClonedArray);
okey, so,
i think what you are talking about is
filter
function notmap
on the array, yes we can use filter to remove an item from the array for sure. and that works its a relative expensive operation tho, because JS have to loop over the entire array.With map, you can create a new array but can't really remove an item in a straightforward manner.
Thanks for the Object.assign() thing, i didn't realize we can use it for arrays too, Good to know
Thank you for talking about time complexity of those methods.
You can use flatMap too
s.flatMap(x => (x === 10) ? [] : x) === s
It is hard to use slice to remove an element, isn't it?
Yeah flatMap is an option, but isn't that unnecessary ?, i mean we can use filter if we just wanna iterate over the full array.
arr.filter(it => it !== 1)
Whenever the condition returns true that item is added to the return array, and when the condition returns false it is not added to the new array. i wouldn't recommend this approach tho, same reason no need to iterate over the full array just to remove an item. Much better to use
findIndex
and splice combination if you don't have the index of which item to remove. withfindIndex
we will only iterate over an array upto the point we find the requested item.You can't really remove an array using
slice
, we can do it withsplice
tho, it's fairly simple developer.mozilla.org/en-US/docs/W...arr.splice(start, length)
problem with react and splice is that it mutates the existing array and with react we don't wanna mutate the original state so we make a copy array first.