store icon indicating copy to clipboard operation
store copied to clipboard

Encapsulation and @select

Open NRaf opened this issue 8 years ago • 3 comments

This is a...

  • [x] usage question

Hi,

I was wondering if there's an alternative approach to implementing selectors that doesn't require us to explicitly expose the shape of the state tree. I feel like having components know about the state shape is breaking encapsulation and will make refactoring a lot more difficult (especially if you're passing strings to @select()).

It seems to me that there's no good reason for components to even know that Redux is being used to manage state in a large application. One way of getting around the issue could be to only use the implement the selection logic in services and simply return observables / values?

That way, in theory, we could potentially switch out Redux without need to make many (any?) changes to components.

Just wondering if I'm missing anything or if there are any gotchas with this approach or if something already exists to handle this problem?

NRaf avatar Dec 02 '17 21:12 NRaf

One approach that I have done, is to pull selectors out to their own file(s), for example something like:

// account-feature-selectors 

export someSelector = (state) => 
export someSelector1 = (state) => 
export someSelector1 = (state) => 

// in the component
import { someSelector } from './selectors-location';

export class SomeComponent {
    @select(someSelector) prop$;    
}

Depending on how ignorant you want your components to be of of redux, could do something like:

@Injectable()
export class FeatureStateService {
 @select(someSelector) prop$; 
}


export class SomeComponent {
   prop$;
   constructor(private featureStateService: FeatureStateService) { }

   ngOnInit() { 
         this.prop$ = this.featureStateService.prop$;
  }
    
}

This way, if you wanted to stop using redux, or wanted to switch to NgRx instead of angular-redux - your state services would be the main thing needing refactoring.

With either approach though - you do make your components less aware of the shape of your state, and can also get reuse of your selectors across various components.

e-schultz avatar Dec 04 '17 16:12 e-schultz

The idea to put selectors in a service is actually awesome. I've made this one:

import { select } from '@angular-redux/store';
import { Injectable, OnDestroy } from '@angular/core';
import { Observable, Subscription } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ReduxService implements OnDestroy {
  @select(state => state) appState$: Observable<any>;

  appState: any = {};

  private linkStateToProp: Subscription;

  constructor() {
    this.linkStateToProp = this.appState$.subscribe((state = {}) => {
      this.appState = state;
    });
  }

  ngOnDestroy() {
    this.linkStateToProp.unsubscribe();
  }
}

This allows me to use {{ appState }} in the template instead of having to use async pipe for every variable.

brunolm avatar Jun 16 '18 00:06 brunolm

I created a package for that and I'm using it here: https://github.com/brunolm/angular-how-to/pull/14

brunolm avatar Jun 17 '18 22:06 brunolm