7

Hi I was wondering if anyone know how to get a store's current state once without having to subscribe to it. I'm currently using ngrx to subscribe to the store and access its state to set a component's attribute, but since I'm subscribed this attribute is constantly refreshing. So I'm looking for a way to obtain this attribute just once so that I can display data without it refreshing constantly.

Just in case, this happens inside my component's constructor.

I've been trying something like this:

_store.select('storeData.correlationData');

When subscribing I would access like this:

_store.subscribe(
  (state) => {
    this.correlationData = state.storeData.correlationData;
  }
);

EDIT

Applciation State:

export interface ApplicationState {
  uiState: UiState;
  storeData: StoreData;
}
Tuco
  • 902
  • 3
  • 18
  • 33

1 Answers1

7

You can create getState() function, put it in a shared module and import where needed. The key is to make it synchronous by using take(1) operator:

export function getState(store: any, selector: string) {
  let _state: any;
  store.take(1).subscribe(o => _state = o);
  return _state;
}

Here's more advanced version I'm using:

export function getState(store: any, selector?: any) {
  let _state: any;
  let state$: any;

  if (typeof selector === 'string' && /\./g.test(selector)) {
    state$ = store.pluck(...selector.split('.'));
  } else if (typeof selector === 'string') {
    state$ = store.map(state => state[selector]);
  } else if (typeof selector === 'function') {
    state$ = store.map(state => selector(state));
  } else {
    state$ = store;
  }
  state$.take(1)
    .subscribe(o => _state = o);
  return _state;
}

With this you can get state in few different ways:

getState(this.store) // all data in Store
getState(this.store, 'users')
getState(this.store, state => state.users)
getState(this.store, 'users.address.street') // Cool!

Use with caution!

As @Maximes pointed out in comments, you should try to use Observables directly in your code and use this method for testing.

Sasxa
  • 40,334
  • 16
  • 88
  • 102
  • Thanks, gonna try this out and get back to you. – Tuco Mar 09 '17 at 22:37
  • 2
    I'm getting a `TypeError: _store.take is not a function`, I'm using `import { Store } from '@ngrx/store'` and passing it like this to my constructor: `private _store: Store` – Tuco Mar 09 '17 at 22:43
  • 2
    Did you `import 'rxjs/add/operator/take';`? – Sasxa Mar 09 '17 at 22:45
  • Thanks that was it! – Tuco Mar 09 '17 at 22:47
  • 4
    `store.take(1).subscribe(o => _state = o);` this code is asynchronous and the variable will be returned without the state. Of course, it's an object so yeah, with the reference you'll have the value at some but ... I don't think it's a good idea. Why don't you just return an `Observable` ? Like `return store.take(1)` and let the user subscribe behind ? (he won't have to unsubscribe thanks to take(1). Second thing is, by doing `getState(this.store, 'users.address.street')`, I don't agree with the "cool" comment as you're loosing type ... – maxime1992 Mar 10 '17 at 09:06
  • @Maxime I usually use `getState()` when prototyping stuff. It's extremely useful when/if you don't know the how exactly your methods will look like. I don't think I have it anywhere in my classes that are "production" ready. Same for `pluck()`, super cool to just pull value from the Store directly into the template like this: `pluck('users.address.street')` while prototyping. – Sasxa Mar 10 '17 at 09:19
  • @Sasxa ok then you should add a warning in your answer saying that's for debug only because people reading it might thinks it's a good idea to use that everywhere. Plus, if you want to avoid that kind of things, you should definitely take a look into https://www.npmjs.com/package/angular2-prettyjson which display a really nice preview of a full object. – maxime1992 Mar 10 '17 at 09:26