Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why is my Angular signal not populating with data from localStorage?

I am using the following function to add user data to cartSignal and store the data to localStorage. It works fine.

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

This is the function that retrieves the data from localStorage:

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())
  }
}

I encounter a problem when restoring cartSignal from localStorage. This is the effect that fires to repopulate cartSignal.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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)
 });

It seems to fail on this line this.cartSignal.set({cartItems:[...cartItems]})

The error is not discernible:

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

>Solution :

The error corresponds to setting a signal inside an effect.

NG0600: Writing to signals is not allowed in a computed or an effect by default. Use allowSignalWrites in the CreateEffectOptions to enable these inside effects.

To solve this problem, you can make use of untracked function which according to docs:

Execute an arbitrary function in a non-reactive (non-tracking) context. The executed function can, optionally, return a value.

Thus, by setting the signal inside this wrapper function, it does not trigger this error, since its a non-reactive (non-tracking) context, the effect does not track the update of this signal.

private appEffect = effect(() => {    
    let cartItems:CartItem[] = this.appService.appSignal().wayfair2.cartItems;
    console.log('cartItems', cartItems)
      untracked(() => { // <- changed here!
        this.cartSignal.set({cartItems:[...cartItems]})
      });
      console.log('cartSignal.cartItems', this.cartSignal().cartItems)
 });

The update method of the signal, is a great function, when you need the current state of the signal, we can change the addToCart function to.

  public addToCart = (item:CartItem) => {
    this.cartSignal.update((cartSignalData: any) => ({
      cartItems: [...cartSignalData.cartItems, item]
    }));
    console.log('cartSignal', this.cartSignal())
  }

The effect method is an awesome way to keep the signal in sync with local storage, with less code.

effect(() => {
  const cartSignalData = this.cartSignal();
  saveStateToLocalStorage(this.cartSignal())
}

Whenever the signal updates, it is guaranteed to be stored in local storage, so you do not have any extra code for this. It reacts on it’s own. But we need to make sure objects are set with new references each time.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading