NGXS技术问询:能否将数组中的对象拆分至子状态?
看起来你已经精准抓住了问题的核心——频繁的find()调用和动作耦合确实会影响代码维护性和性能。按照你的需求,我们可以通过拆分出selectedItem子状态,并实现双向同步逻辑来解决这些问题,同时保留原有的数组和选中ID状态。下面是具体的实现步骤:
1. 更新状态模型结构
首先调整你的顶层状态,新增selectedItem子状态来存储当前选中的完整Item对象:
interface ItemsModel { listOfItems: Item[]; // 保留原数组,最多10个不同项 selections: { itemId: number }; // 保留原选中ID状态 selectedItem: Item | null; // 新增的选中项子状态 }
这样我们就把针对单个Item的操作和数组操作的状态分离开,为后续解耦动作打下基础。
2. 同步选中ID变更到selectedItem
当用户点击切换选中项时,在设置selections.itemId的同时,直接从数组中找到对应Item并更新selectedItem子状态。这样后续操作选中项时就不用反复调用find()了:
@Action(Item.Select) setSelectedItem({ setState, getState }: StateContext<ItemsModel>, { itemId }: Item.Select) { const currentState = getState(); // 只在切换选中时调用一次find() const targetItem = currentState.listOfItems.find(i => i.itemId === itemId) || null; setState(patch({ selections: patch({ itemId }), selectedItem: targetItem })); }
3. 同步selectedItem修改回原数组
接下来,针对选中项的所有修改动作(比如更新数量、修改属性等),我们可以创建专门的动作来处理,同步更新selectedItem子状态和原数组中的对应项:
首先定义一个更新选中项的动作:
export class UpdateSelectedItem implements Action { readonly type = '[Items] Update Selected Item'; constructor(public payload: Partial<Item>) {} }
然后在状态类中处理这个动作,同时更新子状态和原数组:
@Action(UpdateSelectedItem) updateSelectedItem({ setState, getState }: StateContext<ItemsModel>, { payload }: UpdateSelectedItem) { const currentState = getState(); if (!currentState.selectedItem) return; // 无选中项时直接返回 // 更新选中项子状态 const updatedSelectedItem = { ...currentState.selectedItem, ...payload }; // 同步替换原数组中的对应项 const updatedList = currentState.listOfItems.map(item => item.itemId === updatedSelectedItem.itemId ? updatedSelectedItem : item ); setState(patch({ listOfItems: updatedList, selectedItem: updatedSelectedItem })); }
现在所有针对选中项的修改,都可以通过这个动作来处理,完全和数组的其他操作(比如添加、删除项)解耦。
4. 简化Selector(可选)
既然selectedItem已经是状态的直接属性,原来的getSelectedItem选择器可以直接简化,彻底消除find()调用:
@Selector() static getSelectedItem(state: ItemsModel): Item | null { return state.selectedItem; }
额外的状态一致性保障
当执行修改数组的动作(比如删除项)时,要同步检查是否影响了选中状态,避免出现无效的selectedItem:
@Action(Item.Delete) deleteItem({ setState, getState }: StateContext<ItemsModel>, { itemId }: Item.Delete) { const currentState = getState(); const updatedList = currentState.listOfItems.filter(item => item.itemId !== itemId); let updatedSelectedItem = currentState.selectedItem; let updatedSelections = currentState.selections; // 如果删除的是当前选中项,重置选中状态 if (updatedSelectedItem?.itemId === itemId) { updatedSelectedItem = null; // 可以设置为第一个有效项的ID,或者保持0根据你的业务逻辑 updatedSelections = { itemId: updatedList[0]?.itemId || 0 }; } setState(patch({ listOfItems: updatedList, selectedItem: updatedSelectedItem, selections: updatedSelections })); }
通过这些步骤,你既保留了原有的数组和选中ID状态,又实现了选中项子状态的解耦,同时解决了频繁find()的性能问题,所有针对单个Item的操作都可以独立处理,不再和数组操作耦合。
内容的提问来源于stack exchange,提问作者MikeByte

