You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular Signal无法从localStorage加载数据的问题排查

问题:从localStorage恢复cartSignal时触发NG0600错误

以下是正常运行的添加购物车并存储到localStorage的函数:

public addToCart = (item:CartItem) => {
    this.cartSignal.set({cartItems:[...this.cartSignal().cartItems, item]})
    console.log('cartSignal', this.cartSignal())
    saveStateToLocalStorage(this.cartSignal())
}

从localStorage检索数据的AppService代码:

export class AppService {
  public appSignal = signal<{ wayfair2:{cartItems:CartItem[]}}>({wayfair2:{cartItems:  []}})

  public restoreStateFromLocalStorage = () => {
    const wayfair2 = JSON.parse(localStorage.getItem("wayfair2") as string);  
    this.appSignal.set({ wayfair2}) 
    console.log('appSignal', this.appSignal())
  }
}

用于重新填充cartSignal的effect代码,执行时出错:

private appEffect = effect(() => {    
    let cartItems:CartItem[] = this.appService.appSignal().wayfair2.cartItems;
    console.log('cartItems', cartItems)
      this.cartSignal.set({cartItems:[...cartItems]})
      console.log('cartSignal.cartItems', this.cartSignal().cartItems)
 });

报错信息:

ERROR Error: NG0600
w http://localhost:4200/main-3YTPAYWP.js:3
II http://localhost:4200/main-3YTPAYWP.js:7
Ep http://localhost:4200/main-3YTPAYWP.js:1
wc http://localhost:4200/main-3YTPAYWP.js:1
set http://localhost:4200/main-3YTPAYWP.js:7
&lt;anonymous&gt; http://localhost:4200/main-3YTPAYWP.js:8
runEffect http://localhost:4200/main-3YTPAYWP.js:7
watcher http://localhost:4200/main-3YTPAYWP.js:7
a http://localhost:4200/main-3YTPAYWP.js:1
run http://localhost:4200/main-3YTPAYWP.js:7
flushQueue http://localhost:4200/main-3YTPAYWP.js:7
flush http://localhost:4200/main-3YTPAYWP.js:7
invoke http://localhost:4200/polyfills-FFHMD2TL.js:1
onInvoke http://localhost:4200/main-3YTPAYWP.js:7
invoke http://localhost:4200/polyfills-FFHMD2TL.js:1
run http://localhost:4200/polyfills-FFHMD2TL.js:1
flush http://localhost:4200/main-3YTPAYWP.js:7
scheduleEffect http://localhost:4200/main-3YTPAYWP.js:7
invokeTask http://localhost:4200/polyfills-FFHMD2TL.js:1
onInvokeTask http://localhost:4200/main-3YTPAYWP.js:7
invokeTask http://localhost:4200/polyfills-FFHMD2TL.js:1
runTask http://localhost:4200/polyfills-FFHMD2TL.js:1
$ http://localhost:4200/polyfills-FFHMD2TL.js:1
promise callback*H http://localhost:4200/polyfills-FFHMD2TL.js:1
U http://localhost:4200/polyfills-FFHMD2TL.js:1
scheduleTask http://localhost:4200/polyfills-FFHMD2TL.js:1
onScheduleTask http://localhost:4200/polyfills-FFHMD2TL.js:1
scheduleTask http://localhost:4200/polyfills-FFHMD2TL.js:1
scheduleTask http://localhost:4200/polyfills-FFHMD2TL.js:1
scheduleMicroTask http://localhost:4200/polyfills-FFHMD2TL.js:1
o http://localhost:4200/polyfills-FFHMD2TL.js:2
then http://localhost:4200/polyfills-FFHMD2TL.js:2

原因分析

NG0600是Angular的循环更新检测错误,核心诱因有两种:

  1. effect监听appSignal变化,内部修改cartSignal后,cartSignal的更新又触发了appSignal的更新(比如cartSignal变化时执行saveStateToLocalStorage,进而触发restoreStateFromLocalStorage更新appSignal),形成无限循环的更新链。
  2. 每次执行this.cartSignal.set({cartItems:[...cartItems]})时,即使内容一致,新数组的引用会让cartSignal被标记为变化,触发无意义的更新,最终因重复触发导致循环检测报错。

修复方案

方案1:添加内容校验,仅在实际变化时更新

在effect内部对比新旧购物车内容,避免无意义的信号更新:

private appEffect = effect(() => {    
    const newCartItems = this.appService.appSignal().wayfair2.cartItems;
    const currentCartItems = this.cartSignal().cartItems;
    
    // 简单内容对比(复杂对象需用深对比工具)
    if (JSON.stringify(newCartItems) !== JSON.stringify(currentCartItems)) {
      this.cartSignal.set({cartItems: [...newCartItems]})
    }
 });

方案2:用untracked隔离更新操作

如果确定cartSignal的更新不会反向触发appSignal变化,可使用untracked包装set操作,避免Angular误判依赖链:

import { untracked } from '@angular/core';

private appEffect = effect(() => {    
    const cartItems = this.appService.appSignal().wayfair2.cartItems;
    untracked(() => {
      this.cartSignal.set({cartItems: [...cartItems]})
    })
 });

方案3:改用派生信号,从根源避免循环

直接让cartSignal作为appSignal的派生信号,自动同步状态,无需手动effect维护:

// 替换原cartSignal为computed派生信号
public cartSignal = computed(() => ({
  cartItems: [...this.appService.appSignal().wayfair2.cartItems]
}));

// 修改addToCart函数,直接更新appSignal
public addToCart = (item:CartItem) => {
    const currentCartItems = this.appService.appSignal().wayfair2.cartItems;
    this.appService.appSignal.set({
      wayfair2: { cartItems: [...currentCartItems, item] }
    });
    saveStateToLocalStorage(this.appService.appSignal().wayfair2);
}

内容的提问来源于stack exchange,提问作者koque

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 11:53:12