Table of Contents
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:
1body {2 display: flex;3 justify-content: center;4}56.App {7 display: flex;8 flex-direction: column;9 justify-content: center;10 align-items: center;11}1213ul {14 padding: 0;15}1617button {18 margin: 0.5rem;19 cursor: pointer;20}2122ul 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:
1import { useState } from "react"23function getRandomNumber(max = 100) {4 return Math.floor(Math.random() * max)5}6const INITIAL_LIST = Array.from({ length: 5 }, () => getRandomNumber())78function App() {9 const [list, setList] = useState(INITIAL_LIST)1011 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}3637export 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:
1import { useState } from "react"23function getRandomNumber(max = 100) {4 return Math.floor(Math.random() * max)5}6const INITIAL_LIST = Array.from({ length: 5 }, () => getRandomNumber())78function 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}4647export 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 : item5 })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"23function getRandomNumber(max = 100) {4 return Math.floor(Math.random() * max)5}6const INITIAL_LIST = Array.from({ length: 5 }, () => getRandomNumber())78function 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 }2223 const addItemToStart = () => {24 setList(existingItems => {25 return [getRandomNumber(), ...existingItems]26 // return [getRandomNumber()].concat(existingItems);27 })28 }2930 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}5758export 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()56 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 ]89 // 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:
1import { useState } from "react"23function getRandomNumber(max = 100) {4 return Math.floor(Math.random() * max)5}6const INITIAL_LIST = Array.from({ length: 5 }, () => getRandomNumber())78function 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 }2223 const addItemToStart = () => {24 setList(existingItems => {25 return [getRandomNumber(), ...existingItems]26 // return [getRandomNumber()].concat(existingItems);27 })28 }2930 const addItemToEnd = () => {31 setList(existingItems => {32 return [...existingItems, getRandomNumber()]33 // return existingItems.concat([getRandomNumber()]);34 })35 }3637 const deleteItemFromStart = () => {38 setList(existingItems => {39 return existingItems.slice(1)40 // return existingItems.filter((item, i) => i !== 0);41 })42 }4344 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 }5051 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 ]6061 // return existingItems.reduce(62 // (prev, x, i) => prev.concat(i === randomIndex ? [randomNumber, x] : x),63 // []64 // );65 })66 }6768 const removeItemInBetween = () => {69 setList(existingItems => {70 const randomIndex = getRandomNumber(existingItems.length)71 return [72 ...existingItems.slice(0, randomIndex),73 ...existingItems.slice(randomIndex + 1),74 ]7576 // 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}109110export 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 score8 ...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 } : item5 })6 })7}
Here is the complete code using the above functions:
1import { useState } from "react"23const INITIAL_SCORES = [4 { id: 1001, score: 250 },5 { id: 1002, score: 100 },6 { id: 1003, score: 300 },7]89function Scores() {10 const [score, setScore] = useState(INITIAL_SCORES)1112 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 score19 ...existingItems[itemIndex],20 score: existingItems[itemIndex].score + 1,21 },22 ...existingItems.slice(itemIndex + 1),23 ]24 // return existingItems.map((item) => {25 // return item.id === currentId26 // ? { ...item, score: item.score + 1 }27 // : item;28 // });29 })30 }3132 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}4950export default Scores
Do follow me on twitter where I post developer insights more often!