Angular Signals能否存储JS Map?用法及变更检测疑问
问题
我刚开始使用Angular新的Signals API,但听说使用Signals时需要将数据视为不可变类型。我不确定能否用JS Map实现如下操作:
服务代码
// 我的服务中的代码 // 我使用这个Map按队列名保存和获取消息 messages = signal(new Map<string, Object>()); queryForMessagesInQueue(queue) { this.httpClient.get('...').subscribe((newMessagesArray) => { const messages = this.messages().get(queue) || {}; // 验证新消息是否已存在于消息对象中 let addedMessages = 0; newMessagesArray.forEach((newMsg) => { const messageExists = !!messages[newMsg.id] if (messageExists == false) { messages[newMsg.id] = newMsg; addedMessages++; } }); if (addedMessages !== 0) { this.messages.update((msgMap) => msgMap.set(queue, messages)); } }); }
组件代码
export class MyComponent { private service = inject(MyService) messages = service.messages; // 这是服务中的signal }
请问我的用法是否正确?Signals能否用于JS Map这类数据结构?变更检测能否正常工作?
回答
当前用法存在错误
你的代码违反了Signal核心的不可变性原则:- 直接修改了从Signal中取出的
messages对象(messages[newMsg.id] = newMsg),属于对原引用的修改,而非创建新对象。 - 调用
msgMap.set(queue, messages)时,修改的是原Map实例,Signal的update方法要求返回新的状态值,而非修改原状态——Signal通过引用追踪变化,原实例的修改不会触发更新。
修正后的代码示例:
queryForMessagesInQueue(queue) { this.httpClient.get('...').subscribe((newMessagesArray) => { const currentMap = this.messages(); // 创建新的消息对象,避免修改原数据 const existingMessages = currentMap.get(queue) || {}; // 合并新消息,仅保留不存在的条目 const updatedMessages = { ...existingMessages, ...newMessagesArray.reduce((acc, msg) => { if (!existingMessages[msg.id]) { acc[msg.id] = msg; } return acc; }, {}) }; const hasNewMessages = Object.keys(updatedMessages).length > Object.keys(existingMessages).length; if (hasNewMessages) { // 创建新的Map实例,替换原队列数据 this.messages.set(new Map(currentMap).set(queue, updatedMessages)); } }); }- 直接修改了从Signal中取出的
Signals完全支持JS Map这类数据结构
Signal可以存储任意JavaScript类型的值,包括Map、Set、对象、数组等。只要遵循不可变性原则,通过返回新实例的方式更新状态,就能正常使用。当前代码的变更检测无法正常工作
由于你修改的是原Map和消息对象的引用,Signal无法检测到值的变化(引用未发生改变),因此组件不会收到更新通知,视图也不会刷新。只有当你给Signal设置新的Map实例时,才会触发变更通知,组件视图才会同步更新。
内容的提问来源于stack exchange,提问作者Danilo Bassi
相关产品推荐
相关产品推荐

