Alex
Alex

Reputation: 1

Subscribe is firing when data were not changed in store

I have and Angular 5 app with ngrx. I have an array of objects in store. When I subscribe to get this array change event is fired even when I don't change state.details.selectedOptions. I changed only state.details.page and subscriber was called.

When I change state.details.selectedOptions to plain value (string) everything is working as expected. As I see the problem is in arrays/nested elements?

//store
const initialState = {
  page: null,
  selectedOptions: []
}
// selector
export const selectedOptionsSelector =  createSelector(
  (state: any) => state.details.selectedOptions
)
// also tried such variant
export const selectedOptionsSelector = (state: any) => state.details.selectedOptions
// Component
ngOnInit() {
  this.store.select(selectedOptionsSelector).subscribe((res: any) => {
    console.log('selectedOptions', res)
  })
}

Update:

Basically in my case I always have empty array in selectedOptions because initial state didn't change. I tried using distinctUntilChanged in select but it didn't help

Upvotes: 0

Views: 1928

Answers (1)

MartaGalve
MartaGalve

Reputation: 1226

It is hard to know for sure as you have not added your reducer definition, but as you say you always set selectedOptions to an empty array I will assume you mean in your reducer you have an action to update the page, and when the action is dispatched you modify its state as:

case Actions.Type.UPDATE_PAGE: {
    return {
        ...state,
        page: 'newValue',
        selectedOptions: []
    }
}

Redux uses shallow equality checking to determine if the state has changed. As @Roomy says, ["a"] === ["a"]returns false, therefore Redux will see the above as a change to state.selectedOptions as you are assigning a new reference. Using distinctUntilChanged() doesn't make a difference because it uses shallow equality checking, so if you pass 2 arrays with different references, it will consider this a change.

If you only need to update the page, update only the page property and leave selectedOptions as it was:

case Actions.Type.UPDATE_PAGE: {
    return {
        ...state,
        page: 'newValue'
    }
}

This way the observer in your component won't be notified of a change in selectedOptions.

You could also use

case Actions.Type.UPDATE_PAGE: {
            return {
                ...state,
                page: 'newValue',
                selectedOptions: state.selectedOptions
            }
}

because in this case selectedOptions would still point to the same reference. This is pointless though, but it helps explaining the point.

Upvotes: 1

Related Questions