Возможно, вы сталкивались с различными ситуациями, когда вам нужно сохранить массив в состоянии React и позже изменить его. В этой статье мы рассмотрим различные способы сделать это.
- Настройка проекта
- Изменение элемента в массиве
- Добавление элементов в массив
- Добавление элемента в начало массива:
- Добавление элемента в конец массива
- Добавление элемента между элементами массива
- Удаление элементов из массива
- Удаление элемента из начала массива
- Удаление элемента из конца массива
- Удаление элемента между элементами массива
- Окончательный код
- Обновление массива объектов
Настройка проекта
Создайте проект react, выполнив следующую команду:
npx create-react-app react-usestate-array
Обновите файл index.css
следующим кодом для стилизации приложения:
body {
display: flex;
justify-content: center;
}
.App {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
ul {
padding: 0;
}
button {
margin: 0.5rem;
cursor: pointer;
}
ul li {
display: flex;
align-items: center;
list-style-type: disc;
justify-content: space-between;
}
Обновите файл App.js
следующим кодом:
import { useState } from "react"
function getRandomNumber(max = 100) {
return Math.floor(Math.random() * max)
}
const INITIAL_LIST = Array.from({ length: 5 }, () => getRandomNumber())
function App() {
const [list, setList] = useState(INITIAL_LIST)
return (
<div className="App">
<div>
<button>Add Item to Start</button>
<button>Add Item to End</button>
<button>Add Item in between</button>
</div>
<div>
<button>Delete Item from Start</button>
<button>Delete Item from End</button>
<button onClick>Delete Item from between</button>
</div>
<ul>
{list.map((item, i) => {
return (
<li key={i}>
<span>{item}</span>
<button title="increment">+</button>
</li>
)
})}
</ul>
</div>
)
}
export default App
Здесь мы создаем список случайных чисел, инициализируем локальное состояние со списком случайных чисел и отображаем их. Против каждого числа в списке у нас есть кнопка для его увеличения. Также у нас есть кнопки для изменения списка.
Изменение элемента в массиве
Сначала давайте сделаем так, чтобы кнопки увеличения работали:
import { useState } from "react"
function getRandomNumber(max = 100) {
return Math.floor(Math.random() * max)
}
const INITIAL_LIST = Array.from({ length: 5 }, () => getRandomNumber())
function App() {
const [list, setList] = useState(INITIAL_LIST)
const incrementNumber = index => {
setList(existingItems => {
return [
...existingItems.slice(0, index),
existingItems[index] + 1,
...existingItems.slice(index + 1),
]
})
}
return (
<div className="App">
<div>
<button>Add Item to Start</button>
<button>Add Item to End</button>
<button>Add Item in between</button>
</div>
<div>
<button>Delete Item from Start</button>
<button>Delete Item from End</button>
<button onClick>Delete Item from between</button>
</div>
<ul>
{list.map((item, i) => {
return (
<li key={i}>
<span>{item}</span>
<button title="increment" onClick={() => incrementNumber(i)}>
+
</button>
</li>
)
})}
</ul>
</div>
)
}
export default App
Как вы, наверное, знаете, мы не должны напрямую изменять состояние.
Поэтому мы используем обратный вызов, который является вторым аргументом функции setList
. Обратный вызов получает аргумент, который является существующим состоянием, и мы используем метод slice
и операторы spread, чтобы вернуть обновленный массив.
Альтернативный способ — получить обновленный массив с помощью функции map:
const incrementNumber = index => {
setList(existingItems => {
return existingItems.map((item, j) => {
return j === index ? item + 1 : item
})
})
}
Здесь внутри функции map мы проверяем, совпадает ли переданный индекс с текущим индексом, затем увеличиваем число на единицу, в противном случае возвращаем прежнее число.
Добавление элементов в массив
Мы рассмотрим, как добавить элемент в начало, конец и где-то между ними массива.
Добавление элемента в начало массива:
Мы можем добавить элемент с помощью оператора spread, как показано ниже:
const addItemToStart = () => {
setList(existingItems => {
return [getRandomNumber(), ...existingItems]
// return [getRandomNumber()].concat(existingItems);
})
}
Как видно из прокомментированного кода, вы также можете использовать метод concat.
Не забудьте привязать функцию addItemToStart
к обработчику onClick!
import { useState } from "react"
function getRandomNumber(max = 100) {
return Math.floor(Math.random() * max)
}
const INITIAL_LIST = Array.from({ length: 5 }, () => getRandomNumber())
function App() {
const [list, setList] = useState(INITIAL_LIST)
const incrementNumber = index => {
setList(existingItems => {
return [
...existingItems.slice(0, index),
existingItems[index] + 1,
...existingItems.slice(index + 1),
]
// return existingItems.map((item, j) => {
// return j === index ? item + 1 : item;
// });
})
}
const addItemToStart = () => {
setList(existingItems => {
return [getRandomNumber(), ...existingItems]
// return [getRandomNumber()].concat(existingItems);
})
}
return (
<div className="App">
<div>
<button onClick={addItemToStart}>Add Item to Start</button>
<button>Add Item to End</button>
<button>Add Item in between</button>
</div>
<div>
<button>Delete Item from Start</button>
<button>Delete Item from End</button>
<button onClick>Delete Item from between</button>
</div>
<ul>
{list.map((item, i) => {
return (
<li key={i}>
<span>{item}</span>
<button title="increment" onClick={() => incrementNumber(i)}>
+
</button>
</li>
)
})}
</ul>
</div>
)
}
export default App
Добавление элемента в конец массива
Аналогично добавлению элемента в начало массива, мы можем использовать оператор spread, изменяя порядок:
const addItemToEnd = () => {
setList(existingItems => {
return [...existingItems, getRandomNumber()]
// return existingItems.concat([getRandomNumber()]);
})
}
Альтернативный подход с использованием метода concat также может быть использован, как показано в прокомментированном коде выше.
Добавление элемента между элементами массива
Скажем, если вам нужно добавить элемент в определенный индекс, а затем сдвинуть остальные элементы вправо на 1 индекс, вы можете сделать это с помощью оператора slice и spread, как показано ниже:
const addItemInBetween = () => {
setList(existingItems => {
const randomIndex = getRandomNumber(existingItems.length)
const randomNumber = getRandomNumber()
return [
...existingItems.slice(0, randomIndex),
randomNumber,
...existingItems.slice(randomIndex),
]
})
}
Здесь мы случайным образом сгенерировали индекс, вы можете жестко закодировать его на какое-то значение и посмотреть, правильно ли он обновляется.
Мы также можем использовать метод reduce, как показано ниже, для добавления элемента между ними:
const addItemInBetween = () => {
setList(existingItems => {
const randomIndex = getRandomNumber(existingItems.length)
const randomNumber = getRandomNumber()
return existingItems.reduce(
(prev, x, i) => prev.concat(i === randomIndex ? [randomNumber, x] : x),
[]
)
})
}
Здесь внутри обратного вызова метода reduce, если индекс совпадает с индексом обновляемого элемента, то мы конкатенируем предыдущий массив с массивом из номера, который нужно вставить, и текущего элемента. В противном случае мы просто конкатенируем текущий элемент с предыдущим массивом.
Удаление элементов из массива
При удалении мы также рассмотрим, как удалять с начала, конца и между элементами массива.
Удаление элемента из начала массива
Здесь мы также можем использовать метод slice. Когда мы передаем 1 в качестве первого аргумента методу slice, он возвращает все элементы, начиная с первого индекса (все элементы, кроме первого, так как индекс массива начинается с 0).
const deleteItemFromStart = () => {
setList(existingItems => {
return existingItems.slice(1)
// return existingItems.filter((item, i) => i !== 0);
})
}
Как видите, мы также можем использовать метод filter
, где мы проверяем, равен ли индекс 0, и если да, то отфильтровываем его.
Удаление элемента из конца массива
Последний индекс массива можно найти с помощью Array.length - 1
, поэтому для удаления последнего элемента мы можем выполнить Array.slice(0, Array.length - 1)
:
const deleteItemFromEnd = () => {
setList(existingItems => {
return existingItems.slice(0, existingItems.length - 1)
// return existingItems.filter((item, i) => i !== existingItems.length - 1);
})
}
Даже функция filter
также может быть использована, как показано в прокомментированном коде.
Удаление элемента между элементами массива
При удалении из определенной позиции мы можем использовать комбинацию метода slice и оператора spread:
const removeItemInBetween = () => {
setList(existingItems => {
const randomIndex = getRandomNumber(existingItems.length)
return [
...existingItems.slice(0, randomIndex),
...existingItems.slice(randomIndex + 1),
]
// return existingItems.reduce(
// (prev, x, i) => prev.concat(i === randomIndex ? [] : x),
// []
// );
})
}
Как вы можете видеть, мы разложили элементы до индекса и после индекса и добавили их в совершенно новый массив. Этого также можно достичь с помощью метода reduce, аналогично добавлению элемента по указанному индексу, за исключением того, что при совпадении индекса мы конкатенируем пустой массив, тем самым пропуская его.
Окончательный код
Вот окончательный код со всеми операциями вместе:
import { useState } from "react"
function getRandomNumber(max = 100) {
return Math.floor(Math.random() * max)
}
const INITIAL_LIST = Array.from({ length: 5 }, () => getRandomNumber())
function App() {
const [list, setList] = useState(INITIAL_LIST)
const incrementNumber = index => {
setList(existingItems => {
return [
...existingItems.slice(0, index),
existingItems[index] + 1,
...existingItems.slice(index + 1),
]
// return existingItems.map((item, j) => {
// return j === index ? item + 1 : item;
// });
})
}
const addItemToStart = () => {
setList(existingItems => {
return [getRandomNumber(), ...existingItems]
// return [getRandomNumber()].concat(existingItems);
})
}
const addItemToEnd = () => {
setList(existingItems => {
return [...existingItems, getRandomNumber()]
// return existingItems.concat([getRandomNumber()]);
})
}
const deleteItemFromStart = () => {
setList(existingItems => {
return existingItems.slice(1)
// return existingItems.filter((item, i) => i !== 0);
})
}
const deleteItemFromEnd = () => {
setList(existingItems => {
return existingItems.slice(0, existingItems.length - 1)
// return existingItems.filter((item, i) => i !== existingItems.length - 1);
})
}
const addItemInBetween = () => {
setList(existingItems => {
const randomIndex = getRandomNumber(existingItems.length)
const randomNumber = getRandomNumber()
return [
...existingItems.slice(0, randomIndex),
randomNumber,
...existingItems.slice(randomIndex),
]
// return existingItems.reduce(
// (prev, x, i) => prev.concat(i === randomIndex ? [randomNumber, x] : x),
// []
// );
})
}
const removeItemInBetween = () => {
setList(existingItems => {
const randomIndex = getRandomNumber(existingItems.length)
return [
...existingItems.slice(0, randomIndex),
...existingItems.slice(randomIndex + 1),
]
// return existingItems.reduce(
// (prev, x, i) => prev.concat(i === randomIndex ? [] : x),
// []
// );
})
}
return (
<div className="App">
<div>
<button onClick={addItemToStart}>Add Item to Start</button>
<button onClick={addItemToEnd}>Add Item to End</button>
<button onClick={addItemInBetween}>Add Item in between</button>
</div>
<div>
<button onClick={deleteItemFromStart}>Delete Item from Start</button>
<button onClick={deleteItemFromEnd}>Delete Item from End</button>
<button onClick={removeItemInBetween}>Delete Item from between</button>
</div>
<ul>
{list.map((item, i) => {
return (
<li key={i}>
<span>{item}</span>
<button title="increment" onClick={() => incrementNumber(i)}>
+
</button>
</li>
)
})}
</ul>
</div>
)
}
export default App
Обновление массива объектов
Рассмотрим приведенный ниже массив:
[
{ "id": 1001, "score": 250 },
{ "id": 1002, "score": 100 },
{ "id": 1003, "score": 300 }
]
Если у вас есть массив объектов с уникальными идентификаторами, присвоенными каждому объекту, и вы хотите изменить массив на основе идентификатора, то этого можно добиться с помощью следующей функции:
const incrementScore = currentId => {
setScore(existingItems => {
const itemIndex = existingItems.findIndex(item => item.id === currentId)
return [
...existingItems.slice(0, itemIndex),
{
// spread all the other items in the object and update only the score
...existingItems[itemIndex],
score: existingItems[itemIndex].score + 1,
},
...existingItems.slice(itemIndex + 1),
]
})
}
Той же функциональности можно достичь с помощью функции map, как показано ниже:
const incrementScore = currentId => {
setScore(existingItems => {
return existingItems.map(item => {
return item.id === currentId ? { ...item, score: item.score + 1 } : item
})
})
}
Вот полный код, использующий вышеуказанные функции:
import { useState } from "react"
const INITIAL_SCORES = [
{ id: 1001, score: 250 },
{ id: 1002, score: 100 },
{ id: 1003, score: 300 },
]
function Scores() {
const [score, setScore] = useState(INITIAL_SCORES)
const incrementScore = currentId => {
setScore(existingItems => {
const itemIndex = existingItems.findIndex(item => item.id === currentId)
return [
...existingItems.slice(0, itemIndex),
{
// spread all the other items in the object and update only the score
...existingItems[itemIndex],
score: existingItems[itemIndex].score + 1,
},
...existingItems.slice(itemIndex + 1),
]
// return existingItems.map((item) => {
// return item.id === currentId
// ? { ...item, score: item.score + 1 }
// : item;
// });
})
}
return (
<div className="App">
<ul>
{score.map(item => {
return (
<li key={item.id}>
<span>{item.score}</span>
<button title="increment" onClick={() => incrementScore(item.id)}>
+
</button>
</li>
)
})}
</ul>
</div>
)
}
export default Scores