NgRx Selector Properties Alternative

NgRx Selector Properties Alternative: Schlank & Zukunftssicher

Table of Contents

Selektoren sind das zentrale Werkzeug in NgRx, um aus deinem globalen State genau jene Daten herauszufiltern, die deine Komponenten wirklich brauchen. Früher bot NgRx dafür die sog. “selectors with props”-API an, mit der du dynamisch Filterkriterien direkt an den Selector übergeben konntest. Dieser Ansatz ist jedoch inzwischen deprecated und wird in zukünftigen Versionen vollständig entfernt. Damit dein Projekt langfristig wartbar bleibt und du nicht plötzlich vor komplett umgeschriebener Logik stehst, lohnt es sich, jetzt auf eine schlanke NgRx Selector Properties Alternative umzusteigen, die ohne die veraltete Props-API auskommt.

Das Problem

Wenn du ohne Props filterst, landet all deine Filterlogik in der Component-Schicht. Das sieht dann etwa so aus:

// food.selectors.ts
const selectFoodBasket = createSelector((state: AppState) => state.foodBasket);

// my-view.component.ts
…
const fruits$ = store.select(selectFoodBasket).pipe(
  map(basket => basket.find(item => item.foodType === 'fruits'))
);
…

Auf den ersten Blick mag das harmlos wirken, doch in größeren Anwendungen summiert sich der Code: Du wiederholst denselben Filter überall dort, wo du ihn brauchst. Jede Änderung am Filterkriterium bedeutet, dass du dutzende Dateien durchsuchen musst. Außerdem verschleierst du die zentrale Logik: Statt an einer Stelle klar definiert zu sein, verteilt sie sich über viele Komponenten. Das führt zu erhöhtem Pflegeaufwand, unübersichtlichem Code und erhöhtem Fehlerpotenzial, wenn beispielsweise jemand versehentlich fruits falsch schreibt oder ein anderes Property verwendet.

Die Lösung: Function Nesting

Mit Function Nesting erhebst du deine Selektoren in eine Higher-Order-Function und kapselst die Filterlogik direkt in deinem Selector-Modul. So vermeidest du Redundanz und hältst deine Komponenten schlank:

// food.selectors.ts
export const selectFoodBasketByType = (foodType: string) =>
  createSelector(
    (state: AppState) => state.foodBasket,
    basket => basket.find(item => item.foodType === foodType)
  );

Im Component-Code nutzt du das dann so:

// my-view.component.ts
…
const fruits$ = this.store.select(selectFoodBasketByType('fruits'));
…

Was hier passiert:

  1. Parametrisierung: Deine Funktion selectFoodBasketByType nimmt einen Parameter foodType entgegen.
  2. Memoisierung: NgRx cached das Ergebnis basierend auf foodType automatisch, sodass wiederholte Aufrufe blitzschnell zurückgegeben werden.
  3. Zentrale Logik: Filterkriterium und Datenzugriff bleiben im Selector-Modul, während die Component nur noch den passenden Selector aufruft.

Durch diesen Aufbau sparst du nicht nur viele Zeilen Code, sondern sorgst auch für ein konsistentes Verhalten in der gesamten Anwendung. Neue Filterkriterien lassen sich einfach hinzufügen: Schreibe eine weitere Higher-Order-Function oder erweiterte Version derselben Funktion – ohne Komponenten anfassen zu müssen.

Vorteile auf einen Blick

  • DRY & Wiederverwendbar
    Einmal definiert, kannst du deinen parametrisierten Selector überall einsetzen. Änderungen an der Filterlogik gelten sofort global.
  • Typ-Sicherheit durch TypeScript
    Klare Funktionssignatur verhindert Tippfehler und sorgt für frühe Fehlermeldungen schon beim Kompilieren.
  • Performance
    Dank NgRx-Memoisierung wird dein State nur wirklich neu ausgewertet, wenn sich das zugrunde liegende Array oder der Parameter ändert.
  • Wartbarkeit
    Sämtliche Filterregeln befinden sich in einem Modul, Komponenten bleiben schlank und übersichtlich.
  • Testbarkeit
    Unit-Tests für deine Higher-Order-Functions sind einfach aufzusetzen: Du übergibst unterschiedliche Parameter und prüfst das Ergebnis gegen einen Mock-State.

State-Typ

interface AppState {
  foodBasket: { foodType: string; quantity: number }[];
}

Definiere deinen State-Typ klar und eindeutig, damit dein parametrischer Selector exakt weiß, mit welchen Datenstrukturen er arbeitet.

Kompletter Selector

export const selectFoodBasketByType = (foodType: string) =>
  createSelector(
    (state: AppState) => state.foodBasket,
    basket => basket.find(item => item.foodType === foodType)
  );

Diese Higher-Order-Function ist das Herzstück deiner neuen NgRx Selector Properties Alternative. Egal, ob du später Pizza, Snacks oder Getränke filterst – du benötigst nur diese eine Funktion.

Mit diesem Ansatz führst du eine saubere Trennung von Datenlogik und Präsentation ein, reduzierst Boilerplate auf ein Minimum und bist perfekt gerüstet für zukünftige NgRx-Versionen. Deine Komponenten bleiben fokussiert auf UI und Verhalten, während alle komplexen Selektionsaufgaben zuverlässig in deinem Store-Modul abgehandelt werden.

Dev Snacks

#GreenStack Newsletter

Bleibe up to date zum Thema Green Software und erweitere dein Wissen auf deiner #GreenStack Journey:

Teile den Beitrag mit anderen: