Я начал с реакции несколько месяцев назад, и мне показалось, что до сих пор я достаточно хорошо понимал мутации.
У меня есть функция, которая принимает объект, выбранный itemId и parentId (необязательно).Данные могут иметь вложенные элементы до 1 уровня.Что мне нужно сделать, это изменить значение ключа selected
, если itemId совпадает.Если itemId находится внутри родителя, тогда мне нужно установить selected
внутри этого объекта.
Другой вариант использования, если itemId является идентификатором родителя.В этом случае мне нужно также установить значение selected для всех детей.И это где проблема приходит.Когда я пытаюсь установить значение selected
для детей, функция изменяет данные, которые я отправляю, и поэтому возвращает неправильные результаты.
Вот эта функция:
function returnSelectedItems(data, itemId, parentId) {
// created this object trying to rule out mutation problem in the tests. @see tests file.
const originalData = data.map(item => item);
if (parentId) {
// if parent id is present, a child element has been selected, then directly go to the element
// go through all the items
return originalData.map(parentItem => {
// see if the id matches the parent item
if (parentItem.id === parentId) {
// if yes, check if it contains any children
if (parentItem.children && parentItem.children.length > 0) {
// iterate through their children and assign the resulting array as the children for the parent
parentItem.children = parentItem.children.map(childItem => {
// find the child with the id selected and change its value
if (childItem.id === itemId) {
childItem.selected = !childItem.selected;
}
return childItem;
});
}
}
return parentItem;
});
} else {
return originalData.map(item => {
if (item.id === itemId) {
// set the item to the opposite it was before
item.selected = !item.selected;
// if it has children, we have to set the values for all of them.
if (item.children && item.children.length > 0) {
item.children = item.children.map(childItem => {
childItem.selected = item.selected;
return childItem;
});
}
}
return item;
});
}
}
И вот тесты, которые я создал, чтобы проверить, работает ли это как ожидалось:
test('WITHOUT children test for selectedItems function', () => {
const data = [
{
id: 1,
name: 'test',
selected: false
},
{
id: 2,
name: 'test',
selected: false
},
{
id: 3,
name: 'test',
selected: false
},
{
id: 4,
name: 'test',
selected: false
},
{
id: 5,
name: 'test',
selected: false
}
];
expect(returnSelectedItems(data, 4)).toEqual(
data.map(item => {
if (item.id === 4) {
item.selected = true;
}
return item;
})
);
expect(returnSelectedItems(data, 5)).toEqual(
data.map(item => {
if (item.id === 5) {
item.selected = true;
}
return item;
})
);
expect(
returnSelectedItems(
data.map(item => {
if (item.id === 4) {
item.selected = true;
}
return item;
}, 4)
)
).toEqual(data);
expect(
returnSelectedItems(
data.map(item => {
if (item.id === 5) {
item.selected = true;
}
return item;
}, 5)
)
).toEqual(data);
});
test('WITH children test for selectedItems function', () => {
const data = [
{
id: 1,
name: 'test',
selected: false,
children: [{id: 1, name: 'test', selected: false}, {id: 2, name: 'test', selected: false}, {id: 3, name: 'test', selected: false}]
},
{
id: 2,
name: 'test',
selected: false,
children: [{id: 1, name: 'test', selected: false}, {id: 2, name: 'test', selected: false}, {id: 3, name: 'test', selected: false}]
},
{
id: 3,
name: 'test',
selected: false,
children: [{id: 1, name: 'test', selected: false}, {id: 2, name: 'test', selected: false}, {id: 3, name: 'test', selected: false}]
},
{
id: 4,
name: 'test',
selected: false,
children: [{id: 1, name: 'test', selected: false}, {id: 2, name: 'test', selected: false}, {id: 3, name: 'test', selected: false}]
},
{
id: 5,
name: 'test',
selected: false,
children: [{id: 1, name: 'test', selected: false}, {id: 2, name: 'test', selected: false}, {id: 3, name: 'test', selected: false}]
}
];
const backupData = [
{
id: 1,
name: 'test',
selected: false,
children: [{id: 1, name: 'test', selected: false}, {id: 2, name: 'test', selected: false}, {id: 3, name: 'test', selected: false}]
},
{
id: 2,
name: 'test',
selected: false,
children: [{id: 1, name: 'test', selected: false}, {id: 2, name: 'test', selected: false}, {id: 3, name: 'test', selected: false}]
},
{
id: 3,
name: 'test',
selected: false,
children: [{id: 1, name: 'test', selected: false}, {id: 2, name: 'test', selected: false}, {id: 3, name: 'test', selected: false}]
},
{
id: 4,
name: 'test',
selected: false,
children: [{id: 1, name: 'test', selected: false}, {id: 2, name: 'test', selected: false}, {id: 3, name: 'test', selected: false}]
},
{
id: 5,
name: 'test',
selected: false,
children: [{id: 1, name: 'test', selected: false}, {id: 2, name: 'test', selected: false}, {id: 3, name: 'test', selected: false}]
}
];
// this console log is correct
console.log(data.filter(item => item.id === 4)[0]);
const dataBack = returnSelectedItems(data, 4);
// this console is also correct
console.log(dataBack.filter(item => item.id === 4)[0]);
// but here the value of the data is changed
console.log(data.filter(item => item.id === 4)[0]);
// this passes, but it shouldn't
expect(returnSelectedItems(data, 4)).toEqual(
data.map(item => {
if (item.id === 4) {
item.selected = true;
}
return item;
})
);
// this should pass but it does not
expect(backupData).toEqual(data);
});
Спасибо за вашу помощь!