Search code examples
angularrxjsngrxngrx-storengrx-store-4.0

Why is the ngrx Store is being directly updated by changing arrays inside components?


My problem is as follows,

I am subscribing to my store via the following code

export class PrioritySelectComponent implements OnInit, OnDestroy {

  @Input('preset') preset: number; 

  prioritySettingSub: Subscription
  priorities: string[]

  selection: number;

  constructor(
    private store: Store<fromApp.AppState>
  ) { }

  ngOnInit(): void{
    this.prioritySettingSub = this.store.select('projectState').subscribe(data => {
      this.priorities = data.prioritySettings
    })
    if(this.preset !== undefined) {
      this.selection = this.preset
    }
  }

  arrayTest() {
    const potato = '4'
    let newArr = this.priorities
    newArr.push(potato);
  } // this updates the store immediately when run 


  ngOnDestroy(): void{
    this.prioritySettingSub.unsubscribe();
  }

}

Priority Settings is an array that is housed in my store that contains 4 strings 'none', 'low, 'medium', 'high'.

I am making a copy of the array inside my store subscription and using that inside the component. But if I update the copy (priorities), without using a dispatch the store gets instantly updated.

For testing reasons the arrayTest() function is hooked up to a button in the html that fires it on a click event. When clicked '4' gets added to the store array instantly.

This is the project store:

export interface ProjectState {
    projects?: Project[];
    prioritySettings: string[];
    addProjectError: boolean
}

const initialProjectState = {
  projects: [],
  prioritySettings: ['None', 'Low', 'Medium', 'High'],
  addProjectError: false
};

//reducer logic...

this is the html template

<mat-form-field>
  <mat-select placeholder="Priority" [(ngModel)]="selection">
    <mat-option *ngFor="let level of priorities, let i = index" [value]="i">{{level}}</mat-option>
  </mat-select>
</mat-form-field>

<button (click)='arrayTest()' >terst</button>

This does not happen when I do the exact same thing with objects, strings, or numbers

An example of a this method that doesn't break is as follows

Component:

export class TextInputComponent implements OnInit, OnDestroy {


  textsub: Subscription
  textValue: string;

  constructor(
    private store: Store.fromApp<AppState>;
  ) { }

  ngOnInit(): void {
    this.textsub = this.store.select('textinput').subscribe(data => {
      this.textValue = data.textValue
    })
    this.textValue = this.presetValue;
  }

  ngOnDestroy() {
    this.textsub.unsubscribe()
  }

  stringtest(){
    const potato = '4'
    let test = this.textValue
    test = potato;
  }

}

html:

<mat-form-field >
  <input matInput [(ngModel)]="textValue" name="textValue" >
</mat-form-field>

<button (click)='stringtest()' ></button>

When stringtest() is fired the store does not update, and won't unless a dispatch is set up.

This issues with arrays happens in multiple places in my application, I choose this one for its simplicity. In each case it is arrays that cause the problem, why is this the case and how can I fix this behavior?

Thanks in advance!


Solution

  • If you want to make a copy of an array instead of holding onto the original reference (which is why you are having problems), you can use slice() to make a copy:

    this.priorities = data.prioritySettings.slice(0);
    

    Your priorities will now have a new array with all of the same contents. The contents of the array will still have the same references though so if you have an object in there and modify it, you will still be modifying the one in your store.