DlafCreative
DlafCreative

Reputation: 223

Angular2 and Redux : get state in a component

I need to get access to a state inside a Component. Most of the examples show how to consume the state inside the template, using @select and async pipe but not directly in a Component. The needed state is not supposed to change in the time.

I see 3 solutions :

1/ Use getState()

constructor(private ngRedux: NgRedux) { }
ngOnInit() {
    this.prop = this.ngRedux.getState().prop;
    // Do what I need with "this.prop"
}

2/ Move this getter into a Action Creator Service

getProp() {
    return this.ngRedux.getState().prop;
}

3/ Subscribe to the @select() property

@select('stateProp') stateProp$;
property: string;
constructor(private ngRedux: NgRedux) { }
ngOnInit() {
    stateProp$.subscribe(state => this.property = state)
    // Do what I need with "this.property"
}

What's the best way to perform that ?

Thx

Upvotes: 2

Views: 4213

Answers (1)

Richard Matsen
Richard Matsen

Reputation: 23483

There's probably no right or wrong answer to this, but having just added Redux to an Angular app, here's my thoughts.

ngRedux.getState() returns a snapshot, whereas @select() sets up a pipeline that responds every time the state fragment changes.

One of the major reasons for using Redux is to reduce complexity with shared state, in which case we want to use @select in order to respond to changes caused by other components.

Example 3 above is problematic.
Firstly, we can't be sure that the subscription will complete before the rest of the ngOnInit() code executes, so this.property may be uninitialized when we don't expect it to be.

Secondly, do we want a snapshot, in which case use getState(), or do we want a pipeline, in which case move the subsequent code inside the subscribe:

@select('stateProp') stateProp$;
property: string;

constructor(private ngRedux: NgRedux) { }

ngOnInit() {
  stateProp$.subscribe(state => {
    this.property = state;
    // Do what I need with "this.property"
  })
}

The same might be said when we use this.property in other methods.
Seems to me this.property and stateProp$ refer to the same state, so have one or the other but not both.

Aside from constraining the shape of the code, I've found @select() to have some other problems.

Pipeline executes before state is initialized.
Depending on how state is initialized, we may receive an undefined value through the pipeline, e.g

export interface IAppState {
  config?: any;
}

export const initialState: IAppState = {}

@select() config$: any;

ngOnInit() {
  config$.subscribe(config => {
    this.color = config.color;  // Error: Cannot read property 'color' of undefined
                                // May happen if config is obtained async
  }
}

A guard can be used:

ngOnInit() {
  config$
    .filter(data => !!data)
    .subscribe(config => {
      this.color = config.color;
  }
}

or make sure initialState initializes all object levels:

export const initialState: IAppState = { config: {} }

Pipeline continuously executes
If you need to look at existing state before updating it, the subscription will continuously fire. Note, the connection between subscription and dispatch might be subtle (sub-state change, or separation by nested methods):

@select() myDataList$: IDataArray;

myMethod() {
  this.myDataList$.subscribe(myDataList => {
    // Check something on myDataList
    console.log('I will continuously loop');
    this.addDataMethod();
  }
}
addDataMethod() {
  ngRedux.dispatch({type: addSomeDataAction, payload: myData});
}

Using ngRedux.getState() would be the preferred access method, or limit the subscription with .take(1).

Cleaning up subscriptions
Subscriptions in Angular currently need to be unsubscribed in ngOnDestroy() if not completed with a .take(n) or similar operator. Without this step, the subscription continues after the component is gone. Ref: Angular/RxJs When should I unsubscribe from `Subscription`

@select() myData$: IMyData;
private myDataSub;

myMethod() {
  this.myDataSub = this.myData$.subscribe(myData => {
    // Do something with myData
  }
}
ngOnDestroy() {
  this.myDataSub.unsubscribe();
}

Upvotes: 3

Related Questions