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 <anonymous> 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的循环更新检测错误,核心诱因有两种:
- effect监听
appSignal变化,内部修改cartSignal后,cartSignal的更新又触发了appSignal的更新(比如cartSignal变化时执行saveStateToLocalStorage,进而触发restoreStateFromLocalStorage更新appSignal),形成无限循环的更新链。 - 每次执行
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
相关产品推荐
相关产品推荐

