DEV Community

Cover image for Leveling Up State Management with withLinkedState in NgRx v20
Duško Perić
Duško Perić

Posted on

Leveling Up State Management with withLinkedState in NgRx v20

NgRx v20 just dropped, and with it comes a shiny new feature: withLinkedState.

Think of it like a buff for your SignalStore – it lets you create reactive, dependent state that automatically updates when its source changes. Less boilerplate, more focus on the fun stuff.

Imagine you’re building a little League of Legends dashboard. You’ve got a list of champions in your store, and you always want to keep track of the first champion in the list.

Without withLinkedState, you’d probably write some extra logic in your component. With it, you can keep everything in the store:

const ChampionStore = signalStore(
  withState({ champions: ['Nautilus', 'Blitzcrank', 'Leona'] }),
  withLinkedState(({ champions }) => ({
    currentChampion: () => champions()[0],
  })),
  withMethods((store) => ({
    setChampions(champions: string[]): void {
      patchState(store, { champions });
    },
    setCurrentChampion(champion: string): void {
      patchState(store, { currentChampion: champion });
    },
  }))
);
Enter fullscreen mode Exit fullscreen mode

That’s it, currentChampion is just there, reactive and ready.
If the champions array changes, your “selected” champion updates instantly. Swap the list, and your new champion is auto-picked like magic. But if you want to take control, you can use patchState to lock in your pick.

Ok, but what if you don’t just want the first champion, but want to stick to your main even if the champion list changes?

That’s where linkedSignal comes in.

type Champion = { id: number; name: string };

const ChampionStore = signalStore(
  withState({ champions: [] as Champion[] }),
  withLinkedState(({ champions }) => ({
    currentChampion: linkedSignal<Champion, Champion>({
      source: champions,
      computation: (newList, previous) => {
        // Keep your old main if still in the new list
        const currentChamp = newList.find((c) => c.id === previous?.value.id);
        // Otherwise, default to the first champ
        return currentChamp ?? newList[0];
      },
    }),
  }))
);
Enter fullscreen mode Exit fullscreen mode

So if you’re a Nautilus main (anchor’s down! ⚓) and the list updates, your store will keep you locked on him unless he’s removed. If he’s gone, you’ll default to the first champ in line.

withLinkedState feels like unlocking a new rune: less effort, more power, and smoother gameplay when it comes to managing state. It’s a neat addition that makes your store logic more intuitive and future-proof.

Top comments (0)