Skip to content
react

How to store and update arrays in React useState hook

Nov 1, 2021Abhishek EH10 Min Read
How to store and update arrays in React useState hook

You might have come across different use cases where you would want to store an array in the React state and later modify them. In this article, we will see different ways of doing it.

Project setup

Create a react project by running the following command:

1npx create-react-app react-usestate-array

Update the index.css file with the following code for styling the app:

index.css
1body {
2 display: flex;
3 justify-content: center;
4}
5
6.App {
7 display: flex;
8 flex-direction: column;
9 justify-content: center;
10 align-items: center;
11}
12
13ul {
14 padding: 0;
15}
16
17button {
18 margin: 0.5rem;
19 cursor: pointer;
20}
21
22ul li {
23 display: flex;
24 align-items: center;
25 list-style-type: disc;
26 justify-content: space-between;
27}

Update App.js with the following code:

App.js
1import { useState } from "react"
2
3function getRandomNumber(max = 100) {
4 return Math.floor(Math.random() * max)
5}
6const INITIAL_LIST = Array.from({ length: 5 }, () => getRandomNumber())
7
8function App() {
9 const [list, setList] = useState(INITIAL_LIST)
10
11 return (
12 <div className="App">
13 <div>
14 <button>Add Item to Start</button>
15 <button>Add Item to End</button>
16 <button>Add Item in between</button>
17 </div>
18 <div>
19 <button>Delete Item from Start</button>
20 <button>Delete Item from End</button>
21 <button onClick>Delete Item from between</button>
22 </div>
23 <ul>
24 {list.map((item, i) => {
25 return (
26 <li key={i}>
27 <span>{item}</span>
28 <button title="increment">+</button>
29 </li>
30 )
31 })}
32 </ul>
33 </div>
34 )
35}
36
37export default App

Here we are creating a list of random numbers, initializing a local state with the list of random numbers, and displaying them. Against each number in the list, we have a button to increment it. Also, we have buttons to modify the list.

Modifying an item in the array

First, let's make the increment buttons working:

App.js
1import { useState } from "react"
2
3function getRandomNumber(max = 100) {
4 return Math.floor(Math.random() * max)
5}
6const INITIAL_LIST = Array.from({ length: 5 }, () => getRandomNumber())
7
8function App() {
9 const [list, setList] = useState(INITIAL_LIST)
10 const incrementNumber = index => {
11 setList(existingItems => {
12 return [
13 ...existingItems.slice(0, index),
14 existingItems[index] + 1,
15 ...existingItems.slice(index + 1),
16 ]
17 })
18 }
19 return (
20 <div className="App">
21 <div>
22 <button>Add Item to Start</button>
23 <button>Add Item to End</button>
24 <button>Add Item in between</button>
25 </div>
26 <div>
27 <button>Delete Item from Start</button>
28 <button>Delete Item from End</button>
29 <button onClick>Delete Item from between</button>
30 </div>
31 <ul>
32 {list.map((item, i) => {
33 return (
34 <li key={i}>
35 <span>{item}</span>
36 <button title="increment" onClick={() => incrementNumber(i)}>
37 +
38 </button>
39 </li>
40 )
41 })}
42 </ul>
43 </div>
44 )
45}
46
47export default App

As you may be aware, we should not directly modify the state. Hence we use the callback, which is the second argument to setList function. The callback receives an argument, which is the existing state and we make use of the slice method and spread operators to return the updated array.

An alternative way is to get the updated array using map function:

1const incrementNumber = index => {
2 setList(existingItems => {
3 return existingItems.map((item, j) => {
4 return j === index ? item + 1 : item
5 })
6 })
7}

Here inside the map function, we check if the passed index is the same as the current index, then we increment the number by one otherwise we return the same number.

Adding Items to the array

We will see how to add an item to the beginning, end, and somewhere in between the array.

Adding item to the start of the array:

We can add item by using spread operator as shown below:

1const addItemToStart = () => {
2 setList(existingItems => {
3 return [getRandomNumber(), ...existingItems]
4 // return [getRandomNumber()].concat(existingItems);
5 })
6}

As you can see in the commented code, you can use concat method as well.

Don't forget to bind addItemToStart function to the onClick handler!

1import { useState } from "react"
2
3function getRandomNumber(max = 100) {
4 return Math.floor(Math.random() * max)
5}
6const INITIAL_LIST = Array.from({ length: 5 }, () => getRandomNumber())
7
8function App() {
9 const [list, setList] = useState(INITIAL_LIST)
10 const incrementNumber = index => {
11 setList(existingItems => {
12 return [
13 ...existingItems.slice(0, index),
14 existingItems[index] + 1,
15 ...existingItems.slice(index + 1),
16 ]
17 // return existingItems.map((item, j) => {
18 // return j === index ? item + 1 : item;
19 // });
20 })
21 }
22
23 const addItemToStart = () => {
24 setList(existingItems => {
25 return [getRandomNumber(), ...existingItems]
26 // return [getRandomNumber()].concat(existingItems);
27 })
28 }
29
30 return (
31 <div className="App">
32 <div>
33 <button onClick={addItemToStart}>Add Item to Start</button>
34 <button>Add Item to End</button>
35 <button>Add Item in between</button>
36 </div>
37 <div>
38 <button>Delete Item from Start</button>
39 <button>Delete Item from End</button>
40 <button onClick>Delete Item from between</button>
41 </div>
42 <ul>
43 {list.map((item, i) => {
44 return (
45 <li key={i}>
46 <span>{item}</span>
47 <button title="increment" onClick={() => incrementNumber(i)}>
48 +
49 </button>
50 </li>
51 )
52 })}
53 </ul>
54 </div>
55 )
56}
57
58export default App

Adding item to the end of the array

Similar to adding item to the start of the array, we can make use of spread operator by modifying the order:

1const addItemToEnd = () => {
2 setList(existingItems => {
3 return [...existingItems, getRandomNumber()]
4 // return existingItems.concat([getRandomNumber()]);
5 })
6}

An alternative approach with the concat method can also be used as shown in the commented code above.

Adding Item in between of the array

Say if you have to add an item in a particular index and then shift the rest of the items to the right by 1 index, you can do that by using slice and spread operator as shown below:

1const addItemInBetween = () => {
2 setList(existingItems => {
3 const randomIndex = getRandomNumber(existingItems.length)
4 const randomNumber = getRandomNumber()
5 return [
6 ...existingItems.slice(0, randomIndex),
7 randomNumber,
8 ...existingItems.slice(randomIndex),
9 ]
10 })
11}

Here we have randomly generated an index, you can hard code it to some value and see if it is updating correctly.

We can use reduce method as well as shown below for adding an item in between:

1const addItemInBetween = () => {
2 setList(existingItems => {
3 const randomIndex = getRandomNumber(existingItems.length)
4 const randomNumber = getRandomNumber()
5
6 return existingItems.reduce(
7 (prev, x, i) => prev.concat(i === randomIndex ? [randomNumber, x] : x),
8 []
9 )
10 })
11}

Here inside the reduce method callback, if the index is the same as that of the index to be updated, then we concatenate the previous array with an array of the number to be inserted and the current item. Otherwise, we just concatenate the current item to the previous array.

Deleting items from the array

While deleting as well, we will see how to delete from the start, end, and in between the array.

Deleting an item from the start of the array

Here as well we can use the slice method. When we pass 1 as the first argument to the slice method, it returns all the items starting from the first index (all items except the first one, since the array index starts from 0).

1const deleteItemFromStart = () => {
2 setList(existingItems => {
3 return existingItems.slice(1)
4 // return existingItems.filter((item, i) => i !== 0);
5 })
6}

As you can see, we can use the filter method as well, where we check if the index is 0 and if so then we filter it out.

Deleting an item from the end of the array

The last index of the array can be found using Array.length - 1 so in order to remove the last item, we can do Array.slice(0, Array.length - 1):

1const deleteItemFromEnd = () => {
2 setList(existingItems => {
3 return existingItems.slice(0, existingItems.length - 1)
4 // return existingItems.filter((item, i) => i !== existingItems.length - 1);
5 })
6}

Even the filter function can also be used as shown in the commented code.

Deleting an item in between of the array

While deleting from a particular position, we can use the combination of slice method and spread operator:

1const removeItemInBetween = () => {
2 setList(existingItems => {
3 const randomIndex = getRandomNumber(existingItems.length)
4 return [
5 ...existingItems.slice(0, randomIndex),
6 ...existingItems.slice(randomIndex + 1),
7 ]
8
9 // return existingItems.reduce(
10 // (prev, x, i) => prev.concat(i === randomIndex ? [] : x),
11 // []
12 // );
13 })
14}

As you can see, we have spread items before the index and after the index and added them to a brand new array. This can also be achieved using reduce method, similar to adding an item at a specified index, except that we are concatenating an empty array when the index is matched, thus skipping it.

Final code

Here is the final code with all the operations together:

App.js
1import { useState } from "react"
2
3function getRandomNumber(max = 100) {
4 return Math.floor(Math.random() * max)
5}
6const INITIAL_LIST = Array.from({ length: 5 }, () => getRandomNumber())
7
8function App() {
9 const [list, setList] = useState(INITIAL_LIST)
10 const incrementNumber = index => {
11 setList(existingItems => {
12 return [
13 ...existingItems.slice(0, index),
14 existingItems[index] + 1,
15 ...existingItems.slice(index + 1),
16 ]
17 // return existingItems.map((item, j) => {
18 // return j === index ? item + 1 : item;
19 // });
20 })
21 }
22
23 const addItemToStart = () => {
24 setList(existingItems => {
25 return [getRandomNumber(), ...existingItems]
26 // return [getRandomNumber()].concat(existingItems);
27 })
28 }
29
30 const addItemToEnd = () => {
31 setList(existingItems => {
32 return [...existingItems, getRandomNumber()]
33 // return existingItems.concat([getRandomNumber()]);
34 })
35 }
36
37 const deleteItemFromStart = () => {
38 setList(existingItems => {
39 return existingItems.slice(1)
40 // return existingItems.filter((item, i) => i !== 0);
41 })
42 }
43
44 const deleteItemFromEnd = () => {
45 setList(existingItems => {
46 return existingItems.slice(0, existingItems.length - 1)
47 // return existingItems.filter((item, i) => i !== existingItems.length - 1);
48 })
49 }
50
51 const addItemInBetween = () => {
52 setList(existingItems => {
53 const randomIndex = getRandomNumber(existingItems.length)
54 const randomNumber = getRandomNumber()
55 return [
56 ...existingItems.slice(0, randomIndex),
57 randomNumber,
58 ...existingItems.slice(randomIndex),
59 ]
60
61 // return existingItems.reduce(
62 // (prev, x, i) => prev.concat(i === randomIndex ? [randomNumber, x] : x),
63 // []
64 // );
65 })
66 }
67
68 const removeItemInBetween = () => {
69 setList(existingItems => {
70 const randomIndex = getRandomNumber(existingItems.length)
71 return [
72 ...existingItems.slice(0, randomIndex),
73 ...existingItems.slice(randomIndex + 1),
74 ]
75
76 // return existingItems.reduce(
77 // (prev, x, i) => prev.concat(i === randomIndex ? [] : x),
78 // []
79 // );
80 })
81 }
82 return (
83 <div className="App">
84 <div>
85 <button onClick={addItemToStart}>Add Item to Start</button>
86 <button onClick={addItemToEnd}>Add Item to End</button>
87 <button onClick={addItemInBetween}>Add Item in between</button>
88 </div>
89 <div>
90 <button onClick={deleteItemFromStart}>Delete Item from Start</button>
91 <button onClick={deleteItemFromEnd}>Delete Item from End</button>
92 <button onClick={removeItemInBetween}>Delete Item from between</button>
93 </div>
94 <ul>
95 {list.map((item, i) => {
96 return (
97 <li key={i}>
98 <span>{item}</span>
99 <button title="increment" onClick={() => incrementNumber(i)}>
100 +
101 </button>
102 </li>
103 )
104 })}
105 </ul>
106 </div>
107 )
108}
109
110export default App

Updating an array of objects

Consider the below array:

1[
2 { "id": 1001, "score": 250 },
3 { "id": 1002, "score": 100 },
4 { "id": 1003, "score": 300 }
5]

If you have an array of objects with unique ids assigned to each object and you want to modify the array based on the id, then you can achieve it by the following function:

1const incrementScore = currentId => {
2 setScore(existingItems => {
3 const itemIndex = existingItems.findIndex(item => item.id === currentId)
4 return [
5 ...existingItems.slice(0, itemIndex),
6 {
7 // spread all the other items in the object and update only the score
8 ...existingItems[itemIndex],
9 score: existingItems[itemIndex].score + 1,
10 },
11 ...existingItems.slice(itemIndex + 1),
12 ]
13 })
14}

Same functionality can be achieved using map function as shown below:

1const incrementScore = currentId => {
2 setScore(existingItems => {
3 return existingItems.map(item => {
4 return item.id === currentId ? { ...item, score: item.score + 1 } : item
5 })
6 })
7}

Here is the complete code using the above functions:

Scores.js
1import { useState } from "react"
2
3const INITIAL_SCORES = [
4 { id: 1001, score: 250 },
5 { id: 1002, score: 100 },
6 { id: 1003, score: 300 },
7]
8
9function Scores() {
10 const [score, setScore] = useState(INITIAL_SCORES)
11
12 const incrementScore = currentId => {
13 setScore(existingItems => {
14 const itemIndex = existingItems.findIndex(item => item.id === currentId)
15 return [
16 ...existingItems.slice(0, itemIndex),
17 {
18 // spread all the other items in the object and update only the score
19 ...existingItems[itemIndex],
20 score: existingItems[itemIndex].score + 1,
21 },
22 ...existingItems.slice(itemIndex + 1),
23 ]
24 // return existingItems.map((item) => {
25 // return item.id === currentId
26 // ? { ...item, score: item.score + 1 }
27 // : item;
28 // });
29 })
30 }
31
32 return (
33 <div className="App">
34 <ul>
35 {score.map(item => {
36 return (
37 <li key={item.id}>
38 <span>{item.score}</span>
39 <button title="increment" onClick={() => incrementScore(item.id)}>
40 +
41 </button>
42 </li>
43 )
44 })}
45 </ul>
46 </div>
47 )
48}
49
50export default Scores

Do follow me on twitter where I post developer insights more often!

Leave a Comment

© 2021 CodingDeft.Com