CodeMylife
CodeMylife

Reputation: 1611

Ngrx selector not triggering when the reducer alters the slice

I am working on an angular 2 application that uses Ngrx stores extensively. I got a decent structure in my stores. I use selectors to retrieve slices of state and usually all the selectors properly triggers their associated subscriptions. Ngrx is amazing.

Sadly, today I walked into a new issue and I tried looking around on the web for solutions without success. I finally found a work-around myself by testing out different syntax but I am curious as to why this is happening.

This selector is exactly like all my other selectors:

export const getToolBars = (state: State): any => state.toolBars;
export const selectState: MemoizedSelector<object, State> = createFeatureSelector<State>('http');
export const selectToolBars: MemoizedSelector<object, any> = createSelector(selectState, getToolBars);

The problem is that the selector does not trigger the subscription like it should when the data is updated via the reducer.

I found a work-around for this particular case. Using this :

export const getToolBars = (state: State): any => { return { toolBars: state.toolBars }; };

Instead of :

export const getToolBars = (state: State): any => state.toolBars;

fixes it but I would prefer to have all my getters look the same so I wonder why is this issue happening in the first place?

Additional informations, the state.toolBars is an object interfaced as

toolBars: { [name: string]: any };

Any hints or references would do. Right now everything "works" I am only looking for ways to improve my skill set. Thank you very much!

Edit:

I made a stackblitz to illustrate the issue.

https://stackblitz.com/edit/angular-selector-issue?file=src%2Fapp%2Froot-store%2Ftest-store%2Fselectors.ts

On this stackblitz, i made log to console for whenever stuff is being added in the toolBars state by the reducer. Every two seconds, a new entry is being added to the state.

In the current state, the subscription is not triggering when the reducer add content.

If in the selectors.ts we comment line 6 and uncomment line 7, it works as it does in my application. And I am wondering why usual syntax wont...

Upvotes: 7

Views: 7643

Answers (1)

timdeschryver
timdeschryver

Reputation: 15505

You are modifying the state directly, this is the problem.

Selectors are memoized by default and check if the arguments are changed between the last call and the current call. This check is a simple reference check (===). Because you're modifying the state directly, the reference stays the same.

To fix this you can do:

return {
        ...state,
        toolBars: {
          ...state.toolBars,
          [action.value]: action.subValue
        }
}

You can also use ngrx-immer which takes care of this for you.

Upvotes: 8

Related Questions