7

I am working my way through some ngrx tutorials and I think I'm starting to get my brain wrapped around it.

What I don't understand is how to do something as simple as getting a value from the Store:

Goal: Get a value from the store without having to subscribe to it. IE: store.myStoreProperty or store.getValue(<selector>) or ?

From what I understand the only way to grab a value from the store is to do something like this:

  private readonly _store: Store<ApplicationState>;
  // ...
  this._store.select(state => state.currentUser).subscribe(user => {
    if (!user) { return; }
    // ...
  });

Question: Is there any possible way to "instantly" get a value from the store without having to subscribe?

I might just be having trouble wrapping my brain around selectors but I kind of thought that was what they were for. Example from the docs:

import { createSelector } from '@ngrx/store';

export interface FeatureState {
  counter: number;
}

export interface AppState {
  feature: FeatureState;
}

export const selectFeature = (state: AppState) => state.feature;

export const selectFeatureCount = createSelector(
  selectFeature,
  (state: FeatureState) => state.counter
);

In this example, I was thinking that I could just call selectFeature or pass it into this._store.select(selectFeature) to get the actual value, but it returns an observable (and thus wanting you to subscribe to it).

My main use case here is that I want to access current user info throughout the lifecycle of my app. I have an effect that is getting that information from the server, and it all works great. However, I am a little confused on how I can simply just access the value from the store without having to sprinkle .subscribe everywhere.

And yes, I have seen this but it doesn't help me.

Tony Ngo
  • 19,166
  • 4
  • 38
  • 60
mwilson
  • 12,295
  • 7
  • 55
  • 95
  • Have you consider using async pipe ? – Tony Ngo Dec 12 '19 at 03:54
  • If the async pipe isn't an option for you, you could subscribe once in the constructor of a service, assign the emitted value to a public variable in the service and use that variable in your components. But it could happen that you access an outdated value this way and it's generally best to use the reactive approach. – frido Dec 12 '19 at 12:21
  • I'm aware of the `async` pipe. That is specific to the HTML side of the house. I want to get a value from the store (instantly) so I can pass that value into a secondary api call via an angular service. I don't want `.subscribe` everywhere in my code base. – mwilson Dec 12 '19 at 16:30
  • The question is, what is your use case here? Why do you need to get a value from store without subscribing? The nature of the ngrx framework is based upon observable store (state) and I believe that if your requirement is otherwise, then you should use some other state mechanism instead. – Umesh Dec 13 '19 at 06:25
  • 2
    After reading a couple (very) heated (but also entertaining) threads on their github repo with denied feature requests for this kind of functionality, it's not possible. The team seems to be very adamant about not implementing this kind of functionality since it's "not reactive". It took a couple of days to get passed the fact that you have to subscribe to everything, but now I understand why it's that way. (https://github.com/ngrx/store/issues/296) and (https://github.com/ngrx/platform/issues/227) – mwilson Dec 13 '19 at 16:41

3 Answers3

2

How about you add your own subscribe function and call that every time?

  getValue(obj: Observable<any>){
    let value: any;
    obj.subscribe(v => value = v);
    return value;
  }


  showme(){
    console.log(this.getValue(this.user$));     // single value => works
    console.log(this.getValue(this.projects$)); // array => works
  }

Do note that you lose the magic of observables, so when the value changes, it won't update you frontend etc... You will just have the value of the moment in time when asked for the value.

refence to stackblitz demo

The way you're calling you selectors seems wrong you should be importing them like this:

import * as fromAuth from '../../auth/state/reducers';
import * as fromData from '../../data/state/reducers';

And you build your constructor like this:

constructor(private store: Store<fromAuth.State & fromData.State>) {

Your select could then be like this,

this.user$ = store.pipe(select(fromAuth.getUser));
let user = this.getValue(store.pipe(select(fromAuth.getUser))); // this would get you the current user value

Check out the ngrx example app specifically this index page I'm referencing to, to check out selectors. Check out this sample on how they use the selectors and import the state

Normally you build containers and components. You build a container responsible for getting the data from the store, which you then pass with the async pipe to the next container or component which is responsible for the display. With ngOnChanges you can detect changes and update the frontend accordingly.

James D
  • 1,975
  • 2
  • 17
  • 26
0

You can use async pipe like this

export class MyComponent implements OnInit {
    data$: Observable<any>;

    constructor(private store: Store<ApplicationState>) {}

    ngOnInit() {
        this.data$ = this.store.pipe(select(yourSelector));
    }
}

Then in your view

<app-data [data]="data$ | async"></app-data>
Tony Ngo
  • 19,166
  • 4
  • 38
  • 60
  • 1
    That should be the way to go @mwilson. When you need the value elsewhere you should then consider using rxjs operators to combine the value from the observable with others. – MoxxiManagarm Dec 12 '19 at 09:49
  • 1
    combineLatest should be rarely used and not for that at least. You should rather use a selector to combine different parts of the store – maxime1992 Dec 12 '19 at 10:10
  • 1
    As the question states, I want to do this in the component code. I am fully aware of the `async` pipe. I want to get a value from the `store` without having to sprinkle `.subscribe` everywhere in my application. Basically, I want a value from the store, I know it's there, and I want it now without having to subscribe to an observable. So, in the above example, I should be able to do `console.log(this.data$)` and get the actual value of `this.data$`. Not an observable that I have to subscribe to in order to get the data. – mwilson Dec 12 '19 at 16:33
-1

You can use method of state - getValue, to get current value of state

const value: string | undefined = SELECTORS.selectValue(this.state.getValue());