How to subscribe to state changes without using @connectTo

I apologize if this has been covered before. I searched, honest :slight_smile:

How do I do this without using the @connectTo decorator? I need it for a hierarchical state that I’m using.

 // app.ts
  import { pluck } from 'rxjs/operators';
  ...
  
  @connectTo<State>({
    selector: (store) => store.state.pipe(pluck('frameworks')),
    onChanged: 'changeHandler'
  })
  export class App {
    ...
  
    changeHandler(newState: State, oldState: State) {
      console.log('The state has changed', newState);
    }
  }
  
1 Like

So what was the exact trouble you had with normal rxjs subscription approach as described here? The pluck function?

If you take a look at the sample, the subscription is really no different from the subscription passed to connectTo. The changed handler is the subscription handler with the only difference that you only get the next emitted state and the previous one most likely already lies around somewhere in this.state.

this.subscription = this.store.state
  .pipe(pluck('frameworks'))
  .subscribe(
    (state) => this.state = state
  );

If you’d like to have both of them available, so current plus previous you could make use of pairwise and not forgetting to fill in the first fake previous

this.store.state
    .startWith(null) // fill the buffer for the first non-existing previous
    .pairwise()
    .subscribe([previousState,  currentState] => {
        if (previousState === null) {
            console.log('Probably first emission...');
        }
    })
1 Like

Thanks @zewa666. That’s what I needed to know. I thought maybe you magically had the previous state somewhere but I guess you rely on the decorated class’s state property for the previous state.

1 Like

Exactly its just the workaround for familiarity with changedHandlers. So you think we should update the docs for the simple subscription to make clear one can use all the fancy rxjs functions?

1 Like

Yes, I think updating the docs would be great. Thanks.

1 Like

Can I just clarify that store.state.subscription(state => this.state = state); should be disposed?

Looking at your code I note that you set this.subscription = ...

In my code I’ve just been registering the subscription in the ctor()

@observable public state: IState;

constructor(store: Store<IState>)
{
  store.state.subscribe(state => this.state = state);
}

Could this be why I’m getting many, many calls to stateChanged()??

1 Like

Yes you need to store the subscription and dispose it once your component gets unbound/detached.

1 Like

Is there a reason to not subscribe to state in the constructor()?

The reason I ask, is after you answered this question, I went back to the documentation and noticed that it says that you should subscribe/unsubscribe in bind() and unbind().

I went through all of my code adding the subscription to bind(), only to find that I have a lot of places where I depend on the value of state in activate().

Since bind() is called after activate(), of course everything is now broken.

It’s not a big deal to reset my code, but I’m curious now as to where I should perform the subscription?

1 Like

The place for subscribing doesnt really matter tbh. Going with bind helps mentally to also think about unbind wheras there is no destructor. Doing it inside attached though could trigger a rerender but even that might be neglectable depending on your use-case

1 Like