DEV Community

Cover image for Announcing Events Plugin for NgRx SignalStore: A Modern Take on Flux Architecture

Announcing Events Plugin for NgRx SignalStore: A Modern Take on Flux Architecture

Marko Stanimirović on May 12, 2025

We are pleased to announce the introduction of the Events plugin for NgRx SignalStore, released as part of NgRx v19.2! This new addition brings the...
Collapse
 
frankitch profile image
Frankitch • Edited

Yeah, what a great feature!! Thanks!

For a future NGRX article, would love to see how Signal Store could combine with the new angular httpResource 😉

Collapse
 
antoniosantana profile image
AntonioSantanaCorp

Awesome!!! I love this plugin!

Collapse
 
draylegend profile image
Vladimir Drayling

by using on we don't have the access to the prev state. Now only the payload is available

Collapse
 
markostanimirovic profile image
Marko Stanimirović NgRx

It's possible of course. Check the "Performing State Changes" section:

Each case reducer receives the event and returns the state update. It supports three forms of return values: a partial state object, a partial state updater, and an array of partial state objects and/or updaters.

Example:

const incrementBy = event('incrementBy', type<number>());

const CounterStore = signalStore(
  withState({ count: 0 }),
  withReducer(
    on(
      incrementBy,
      ({ payload }) => (state) => ({ count: state.count + payload })
    ),
  )
Enter fullscreen mode Exit fullscreen mode
Collapse
 
thedevtoni profile image
Anthony Diaz • Edited

Any chance a couple of example with updateEntity or setEntity can be added to the article?

I'm fighting with this but keep getting type errors.

 on(ExperimentRunEvents.toggleRunSelection, ({ payload }) => (state) => {
      const run = state.entityMap[payload.sheetManifestId];

      if (!run) {
        return state;
      }

      return setEntity(
        {
          ...run,
          selected: !run.selected,
        },
        { selectId },
      );
    })

Enter fullscreen mode Exit fullscreen mode

Image description

Thread Thread
 
markostanimirovic profile image
Marko Stanimirović NgRx

The state updater needs to be returned directly to the case reducer handler as a result. In your example, the state updater is returned from another state updater. To fix it, you should execute the updater returned by the setEntity function:

return setEntity(
  /* ... */
)(state); // 👈
Enter fullscreen mode Exit fullscreen mode

This can be simplified if the case reducer handler has the current state as the second argument:

on(ExperimentRunEvents.toggleRunSelection, ({ payload }, state) => {
  // ...

  return setEntity(/* ... */);
})
Enter fullscreen mode Exit fullscreen mode

If you want, feel free to open a feature request for this in the NgRx repo: github.com/ngrx/platform/issues

Collapse
 
draylegend profile image
Vladimir Drayling

I'm such a noob XD thank you <3

Collapse
 
tim_hardy_11018f2992c1279 profile image
Tim Hardy

Just trying this out. It looks like a great start.

One discomfort I have (I don't have a good solution for this yet) is...

withReducer(
on(usersPageEvents.opened, usersPageEvents.refreshed, setPending)

There's a coupling there between the reducer and the effects. The reducer "just knows" that the effects are going to do something that involves starting an async process that should cause the "Pending" state to get set. This just feels wrong. It's intelligence/knowledge that the reducer has about the effects and what they're going to do, and that entire concept feels wrong. What if we decide in the future that the usersPage opening should no longer automatically fetch users? The point and benefit of abstractions is that they have no knowledge of what happens "when a page opens" or somesuch.

I don't have a good solution for this yet, but I just wanted to voice it. The only solution at all that comes to mind is an extra event that more explicitly states we are fetching users (that gets dispatched as a result of a page opening), but that seems like it would get very verbose. I haven't decided what would feel good here, but I wanted to voice the discomfort.

Collapse
 
tom_white_ec9c64700e677a9 profile image
Tom White

In large apps that are slowly migrating from NgRx Store to NgRx Signal Store, is there a method to get a Signal Store Effect to listen to NgRx Store Actions?
I know it's early days, but if the team can't provide this, many people will create a lot of ugly code.

Collapse
 
draylegend profile image
Vladimir Drayling

Triggering effects:
provideEnvironmentInitializer(() => inject(RouterStore))

Collapse
 
tim_hardy_11018f2992c1279 profile image
Tim Hardy

Is there an example repo with all of this working in it?