【发布时间】:2021-03-01 06:28:57
【问题描述】:
从服务器返回的数据结构
[
{
id: 1,
type: "Pickup",
items: [
{
id: 1,
description: "Item 1"
}
]
},
{
id: 2,
type: "Drop",
items: [
{
id: 0,
description: "Item 0"
}
]
},
{
id: 3,
type: "Drop",
items: [
{
id: 1,
description: "Item 1"
},
{
id: 2,
description: "Item 2"
}
]
},
{
id: 0,
type: "Pickup",
items: [
{
id: 0,
description: "Item 0"
},
{
id: 2,
description: "Item 2"
}
]
}
];
- 每个元素代表一个事件。
- 每个事件只是一个拾取或丢弃。
- 每个事件可以有一个或多个项目。
初始状态
在初始加载时,循环来自服务器的响应,并为每个事件、每个项目添加一个名为 isSelected 的额外属性,并将其默认设置为 false。 -- 完成。
这个 isSelected 属性仅用于 UI 目的,它告诉用户哪些事件和/或项目已/已被选择。
// shove the response coming from the server here and add extra property called isSelected and set it to default value (false)
const initialState = {
events: []
}
moveEvent 方法:
const moveEvent = ({ events }, selectedEventId) => {
// de-dupe selected items
const selectedItemIds = {};
// grab and find the selected event by id
let foundSelectedEvent = events.find(event => event.id === selectedEventId);
// update the found event and all its items' isSelected property to true
foundSelectedEvent = {
...foundSelectedEvent,
isSelected: true,
items: foundSelectedEvent.items.map(item => {
item = { ...item, isSelected: true };
// Keep track of the selected items to update the other events.
selectedItemIds[item.id] = item.id;
return item;
})
};
events = events.map(event => {
// update events array to have the found selected event
if(event.id === foundSelectedEvent.id) {
return foundSelectedEvent;
}
// Loop over the rest of the non selected events
event.items = event.items.map(item => {
// if the same item exists in the selected event's items, then set item's isSelected to true.
const foundItem = selectedItemIds[item.id];
// foundItem is the id of an item, so 0 is valid
if(foundItem >= 0) {
return { ...item, isSelected: true };
}
return item;
});
const itemCount = event.items.length;
const selectedItemCount = event.items.filter(item => item.isSelected).length;
// If all items in the event are set to isSelected true, then mark the event to isSelected true as well.
if(itemCount === selectedItemCount) {
event = { ...event, isSelected: true };
}
return event;
});
return { events }
}
就我个人而言,我不喜欢我实现 moveEvent 方法的方式,即使我使用的是 find、filter 和 map,它似乎也是一种命令式的方法。 这个 moveEvent 方法所做的就是翻转 isSelected 标志。
- 有更好的解决方案吗?
- 有没有办法减少循环次数?也许事件应该是一个对象,甚至是它的项目。至少,查找事件的查找速度会很快,而且我最初不必使用 Array.find。但是,我仍然必须循环遍历其他非选定事件的属性,或者使用 Object.entries 和/或 Object.values 来回转换它们。
- 还有更多功能性方法吗?递归可以解决这个问题吗?
使用和结果
// found the event with id 0
const newState = moveEvent(initialState, 0);
// Expected results
[
{
id: 1,
type: 'Pickup',
isSelected: false,
items: [ { id: 1, isSelected: false, description: 'Item 1' } ]
}
{
id: 2,
type: 'Drop',
// becasue all items' isSelected properties are set to true (even though it is just one), then set this event's isSelected to true
isSelected: true,
// set this to true because event id 0 has the same item (id 1)
items: [ { id: 0, isSelected: true, description: 'Item 0' } ]
}
{
id: 3,
type: 'Drop',
// since all items' isSelected properties are not set to true, then this should remain false.
isSelected: false,
items: [
{ id: 1, isSelected: false, description: 'Item 1' },
// set this to true because event id 0 has the same item (id 2)
{ id: 2, isSelected: true, description: 'Item 2' }
]
}
{
id: 0,
type: 'Pickup',
// set isSelected to true because the selected event id is 0
isSelected: true,
items: [
// since this belongs to the selected event id of 0, then set all items' isSelected to true
{ id: 0, isSelected: true, description: 'Item 0' },
{ id: 2, isSelected: true, description: 'Item 2' }
]
}
]
【问题讨论】:
-
谢谢。我已经在其他网站上发布了。
-
我认为您的代码过于复杂是因为您将
selectedEventId和isSelectedinside 存储在事件对象中。我们很难在没有看到您的 UI 代码的情况下推荐解决方案,但我建议将selectedEventId移动到状态的根目录(与events: []处于同一级别)。这将大大减少状态排列的数量,因此减少错误的机会。然后可以通过查找selectedEventId来增强组件以显示正确的 UI。始终尝试使状态尽可能简单:) -
是的,UI 太复杂了,无法解释,我在这里过度简化了。我不明白为什么我需要在 state 中添加 selectedEventId。 moveEvent 只是一个接收状态和选定事件 id 并返回新状态的函数。
-
用户可以选择的不仅仅是一个事件。因此,每次用户选择特定事件并更新状态时,都会调用 moveEvent 方法。
标签: javascript arrays object data-structures functional-programming