Реактивность массива объектов в React

Давайте теперь изучим реактивность массива объектов. Как правило, вы будете работать с такой структурой достаточно часто. Поэтому вам необходимо знать, как иммутабельно осуществлять добавление, изменение и удаление элементов такого массива.

Проблема здесь в том, что все изменения следует проводить по id, которые хранятся внутри самих объектов. Из-за этого нельзя просто взять и получить элемент по его id как по ключу массива.

Придется перебирать массив циклом и в цикле проверять каждый из объектов на то, равен ли его id тому, который нам нужен. Если равен, то выполним с ним нужную нам операцию, а если не равен - то оставим элемент без изменения.

Давайте посмотрим на примерах. Пусть у нас есть следующий массив объектов:

const initNotes = [ { id: 'GYi9G_uC4gBF1e2SixDvu', prop1: 'value11', prop2: 'value12', prop3: 'value13', }, { id: 'IWSpfBPSV3SXgRF87uO74', prop1: 'value21', prop2: 'value22', prop3: 'value23', }, { id: 'JAmjRlfQT8rLTm5tG2m1L', prop1: 'value31', prop2: 'value32', prop3: 'value33', }, ]; function App() { }

Давайте выведем каждый элемент нашего массива в отдельном абзаце, а значения свойств каждого объекта - в своем span внутри абзаца:

function App() { const [notes, setNotes] = useState(initNotes); const result = notes.map(note => { return <p key={note.id}> <span>{note.prop1}</span>, <span>{note.prop2}</span>, <span>{note.prop3}</span> </p>; }); return <div> {result} </div>; }

Удаление

Пусть в переменной хранится id элемента массива:

const id = 'IWSpfBPSV3SXgRF87uO74';

Давайте удалим элемент с таким id. Используем для этого метод filter:

setNotes(notes.filter(note => { if (note.id !== id) { return note; } }));

Код можно упростить:

setNotes(notes.filter(note => note.id !== id));

Пусть в переменной хранится id элемента. Сделайте кнопку, которая будет удалять элемент с заданным id.

Добавление

Пусть в переменной хранится объект, который мы хотим сделать новым элементом нашего массива:

const newElem = { id: 'GMNCZnFT4rbBP6cirA0Ha', prop1: 'value41', prop2: 'value42', prop3: 'value43', };

Для этого можно добавить элемент в копию массива:

const copy = Object.assign([], notes); copy.push(newElem); setNotes(copy);

Либо воспользоваться деструтуризацией:

setNotes([...notes, newElem]);

Под абзацами сделайте кнопку, по нажатию на которую в наш массив будет добавляться новый элемент. Пусть при каждом нажатии на кнопку для добавляемого элемента генерируется новый id.

Изменение

Пусть мы хотим изменить какой-нибудь элемент массива. Пусть новые данные хранятся в переменной, например, вот такие:

const data = { id: 'IWSpfBPSV3SXgRF87uO74', prop1: 'value21 !', prop2: 'value22 !', prop3: 'value23 !', };

В приведенном объекте id совпадает с id второго элемента массива, а значения свойств - другие. Говоря другими словами в data в свойстве id у нас хранится id того элемента массива, который мы хотим изменить.

Давайте выполним это изменение. Для этого будем перебирать элементы массива циклом и, если id совпадает с искомым, выполним замену элемента, а если не совпадает, оставим элемент без изменений:

setNotes(notes.map(note => { if (note.id === data.id) { return data; } else { return note; } }));

Можно сократить код, воспользовавшись тернарным оператором:

setNotes(notes.map(note => note.id === data.id ? data : note));

Сделайте кнопку, по нажатию на которую будет изменен элемент массива с id, равным 'JAmjRlfQT8rLTm5tG2m1L'.

Изменение одного свойства

Вам может потребоваться изменять не весь объект, а конкретное свойство. Давайте посмотрим, как это делается.

Пусть в переменных хранятся id элемента, имя свойства для изменения и новое значение свойства:

const id = 'IWSpfBPSV3SXgRF87uO74'; const prop = 'prop1'; const value = '!!!';

Для решения задачи удобно использовать деструктуризацию и вычисляемые имена свойств:

setNotes(notes.map(note => { if (note.id === id) { return {...note, [prop]: value}; } else { return note; } }));

Даны следующие переменные:

const id = 'JAmjRlfQT8rLTm5tG2m1L'; const prop = 'prop2';

Сделайте кнопку, по нажатию на которую будет браться элемент массива с указанным id, в нем будет браться свойство с указанным именем и в конец значения этого свойства будет дописываться знак '!'.

Пусть даны две переменные с именами свойств:

const id = 'JAmjRlfQT8rLTm5tG2m1L'; const prop1 = 'prop2'; const prop2 = 'prop3';

Модифицируйте предыдущую задачу так, чтобы по клику изменения выполнялись сразу для двух указанных свойств.

Переделайте приведенное в теории решение через копирование объекта с помощью Object.assign.

Получение элемента

Вам может потребоваться получить элемент массива по его id. Давайте посмотрим, как это делается.

Пусть id элемента хранится в переменной:

const id = 'IWSpfBPSV3SXgRF87uO74';

Давайте получим элемент с таким id. Используем для этого метод reduce:

const result = notes.reduce((res, note) => { if (note.id === id) { return note; } else { return res; } }, {});

Код можно сократить:

const result = notes.reduce((res, note) => note.id === id ? note: res, {});

Получение значение свойства элемента

Вам может потребоваться получить элемент по id, а затем извлечь из этого элемента значение определенного свойства. Давайте посмотрим, как это делается.

Пусть id элемента и необходимое свойство хранятся в переменных:

const id = 'IWSpfBPSV3SXgRF87uO74'; const prop = 'prop1';

Для решения задачи нужно просто модифицировать полученный ранее код с reduce:

const result = notes.reduce((res, note) => { if (note.id === id) { return note[prop]; // получаем заданное свойство } else { return res; } }, '');

А вот сокращенный вариант:

const result = notes.reduce((res, note) => note.id === id ? note[prop] : res, '');

Даны следующие переменные:

const id = 'JAmjRlfQT8rLTm5tG2m1L'; const prop = 'prop2';

Сделайте кнопку, по нажатию на которую в какой-нибудь абзац из объекта с указанным id выведется значение свойства с указанным именем.

Следующие уроки

В следующих уроках мы будем разбирать реактивность массива объектов подробнее и посвятим отдельные уроки для разбора удаления, добавления, изменения массива объектов.

Для краткости я все примеры буду писать для массива initNotes из начала данного урока, а для всех задач я буду использовать следующий массив продуктов, в котором id() - созданная в предыдущем уроке функция для генерации id:

const initProds = [ {id: id(), name: 'prod1', catg: 'catg1', cost: 100}, {id: id(), name: 'prod2', catg: 'catg2', cost: 200}, {id: id(), name: 'prod3', catg: 'catg3', cost: 300}, ];

Возьмите массив с продуктами initProds и выведите его в виде HTML таблицы. На основе этой таблицы решайте задачи следующих уроков.