Изменение одного элемента
Часто состояние — это массив объектов, и один из них нужно изменить. Вот сет-лист «Ночных поездов»: песни, которые они сыграют, у каждой есть поле played:
const [songs, setSongs] = useState([
{ id: 1, title: "Последний поезд домой", played: false },
{ id: 2, title: "Полуночный экспресс", played: false },
{ id: 3, title: "Пар и дым", played: false },
]);Чтобы отметить одну песню как сыгранную, нужен новый массив, в котором эта песня — новый объект с played: true, а остальные песни такие же, как были. Для этого подходит map() с условием:
function handlePlayed(id) {
setSongs((songs) =>
songs.map((song) =>
song.id === id ? { ...song, played: true } : song,
),
);
}map() создаёт новый массив той же длины. Для каждой песни её стрелочная функция возвращает то, что попадёт в новый массив: для песни с этим id — копию с played: true, а для всех остальных — саму песню без изменений. React получает новый массив, отрисовывает компонент, и страница показывает песню как сыгранную.
Не сработает то, что можно написать первым делом: найти песню и изменить её, song.played = true;. Это меняет объект внутри состояния на месте, как и push(), поэтому React не получает нового значения, и страница не меняется.
Теперь у вас есть все три приёма: spread, чтобы добавить элемент в массив из состояния, filter(), чтобы удалить его, и map(), чтобы изменить один элемент.
Задание
- 1
Клик по кнопке «Сыграна» отмечает свою песню как сыгранную. Напишите
handlePlayed(id), которая с помощьюmap()создаёт новый массив, где у этой песниplayed: true. Стиль зачёркивает сыгранные песни. - 2
Сделайте так, чтобы кнопка работала в обе стороны. У сыгранной песни она называется «Отменить», и клик снова отмечает песню как не сыгранную. (Противоположное значение — это
!song.played.)