Aleksandr
Aleksandr

Reputation: 2321

React to nested state change in Angular and NgRx

Please consider the example below

// Example state
let exampleState = {
  counter: 0;
  modules: {
    authentication: Object,
    geotools: Object
  };
};

class MyAppComponent {
  counter: Observable<number>;
  constructor(private store: Store<AppState>){
    this.counter = store.select('counter');
  }
}

Here in the MyAppComponent we react on changes that occur to the counter property of the state. But what if we want to react on nested properties of the state, for example modules.geotools? Seems like there should be a possibility to call a store.select('modules.geotools'), as putting everything on the first level of the global state seems not to be good for overall state structure.

Update

The answer by @cartant is surely correct, but the NgRx version that is used in the Angular 5 requires a little bit different way of state querying. The idea is that we can not just provide the key to the store.select() call, we need to provide a function that returns the specific state branch. Let us call it the stateGetter and write it to accept any number of arguments (i.e. depth of querying).

// The stateGetter implementation
const getUnderlyingProperty = (currentStateLevel, properties: Array<any>) => {
  if (properties.length === 0) {
    throw 'Unable to get the underlying property';
  } else if (properties.length === 1) {
    const key = properties.shift();
    return currentStateLevel[key];
  } else {
    const key = properties.shift();
    return getUnderlyingProperty(currentStateLevel[key], properties);
  }
} 

export const stateGetter = (...args) => {
  return (state: AppState) => {
    let argsCopy = args.slice();
    return getUnderlyingProperty(state['state'], argsCopy);
  };
};

// Using the stateGetter
...
store.select(storeGetter('root', 'bigbranch', 'mediumbranch', 'smallbranch', 'leaf')).subscribe(data => {});
...

Upvotes: 4

Views: 1749

Answers (1)

cartant
cartant

Reputation: 58440

select takes nested keys as separate strings, so your select call should be:

store.select('modules', 'geotools')

Upvotes: 7

Related Questions