What is the intended way of using computed signals alongside ngrx selectors and where to put the logic?

1.6k Views Asked by At

I am using Angular 16 and just started with signals. Those make working with ngrx even better. When transitioning I found myself presented with something that can be abstracted to this:

const state = {
 someArray: [1, 5, 7]
}

export const selectArray = createSelector(
  state,
  (state) => state.someArray
);

export const selectFirst = createSelector(
  selectArray,
  (someArray) => someArray[0]
);

export const selectLast = createSelector(
  selectArray,
  (someArray) => someArray[state.someArray.length-1]
);

export const selectLastMinusFirst = createSelector(
  [selectFirst, selectLast],
  (first, last) =>
    last - first
);

Now with signals this could be rewritten using computed signals in the component that wants to use it to something like this:

const someArray: Signal<number[]> = this.store.selectSignal(Selectors.SelectArray)
const first: Signal<number> = computed(() => someArray()[0]);
const last: Signal<number> = computed(() => someArray()[state.someArray.length-1]);
const lastMinusFirst: Signal<number> = computed(() => last() - first());

Or even using signals effects:

const first = 0;
const last = 0;
const lastMinusFirst = 0;

const someArray: Signal<number[]> = this.store.selectSignal(Selectors.SelectArray)

constructor() {
  effect(() => {
    if(someArray().length) {
      this.first = someArray()[0];
      this.last = someArray()[someArray().length - 1];
      this.lastMinusFirst = this.last - this.first;
    }
  });
}

What is the intended way of doing it? Are computed signals more efficient as they mention lazy evaluation and memoized?

1

There are 1 best solutions below

0
On

computed() is better than a selector, because

  1. Its dependencies are dynamic [1];
  2. Resulted signal can be used in the template directly and will take care of the change detection without |async or dirty checking;
  3. Dependencies are recursive - if inside computed() you call some function, that also uses signals, then the resulting signal will be updated when a signal in that function is updated. And this can happen somewhere deep in the stack, as long as it happens synchronously [2]. It allows to create very flexible and powerful things - derived signals, composed of derived signals.

Meanwhile, NgRx is working on integration with Angular Signals:


[1], [2] - You can read more about this topic in my free article: Dependency Graph in Angular Signals