-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Add events
plugin to @ngrx/signals
#4580
Comments
Well, I don't see ANY reason why this shouldn't land. 💯👍 |
Very nice! 👏🏿 I wonder: any specific reason why there isn't a |
Hurray! 🚀 Redux-like state handling is the missing part - at least for us and our project 💯 |
Is it something like global actions? I mean, actions that any store can intercept. |
Great work @markostanimirovic as @rainerhahnekamp stated just ship it 😉👍 |
@markostanimirovic looks fantastic |
Damn it, so I'll be the one "against" 😉 The main selling point behind NGRX-SS for me is its simplicity. The core (withState, withComputed, withMethods) is basically getting native angular stuff - but organizing it and making composable. Simple + composable + thin layer over native APIs -> win. Nobody enforces to use a "plugin". But if majority of the community walked in redux style direction, I'd see it as a step backwards in terms of simplicity. Redux style includes significant tradeoffs and it does make sense to be used - but not in all scenarios. From my personal experience, I've seen redux far many more times abused than used properly. Hope that doesn't happen to NGRX signal store. |
Looks great! What about returning multiple events in an effect, similar to using |
Great work as always, @markostanimirovic. This is exactly what we need and turns the Signal Store into a perfect state management solution, where devs may start lean and add the event-based API if needed later.
Defining Events as with-Feature inside a Signal Store definition would not be a good idea as it leads to coupling of a single Signal Store instance to dispatch Events, which need to have a global nature in Redux-like patterns.
Self-dispatching means, that you do not need to call dispatch to pass in an Event, but just execute a method, pass in a payload if needed and the dispatching works automatically behind the covers.
Yes, exactly that. |
@rainerhahnekamp and @markostanimirovic so so goooood🔥❤️ Having back a global event bus is so important🛠️ New people will use anyway signals in angular cause of angular docs and so will choose first the non redux way. so no worries about going in the wrong direction as those with redux needs would stay at ngrx store. this plugin just helps going in the right direction! |
I think it's also a nice idea, if someone wants to inject a store that has both dispatch and get-data functionality, then its possible to define: export const injectUserStore = () => ({ ...inject(UserStore), ...injectDispatch(userEvents) }); Maybe it would be nice to include some store-feature that merges action dispatcher into store or util / docs about it e.g. export const injectStoreWithDispatch = <
T extends Type<StateSource<any>>,
EventGroup extends Record<string, EventCreator | EventWithPropsCreator>,
>(
store: T,
events: EventGroup
): Prettify<InjectDispatchResult<EventGroup> & InstanceType<T>> => ({
...inject<InstanceType<T>>(store),
...injectDispatch(events),
}); |
Just discovered this feature at ng poland conf. This seems to be a game changer for our team for migration from global store to signal store. Waiting to be landed 🔥 |
Hi @markostanimirovic I think we should already consider a possibility to connect redux dev-tools. I have a working hacky solution here, that could be used as an inspiration, but with the ability to change @ngrx internal packages, it would be possible to easily create a fully functional and quality solution. |
I like that events can be created outside of the store 🤩 |
this is (when i understand you @CrazyJoker correctly) - see here https://ngrx.io/guide/eslint-plugin/rules/no-multiple-actions-in-effects - not recommended. |
could |
I have mixed feelings about this. This is a great implementation of a redux approach to signal store, but I agree with @ducin, this takes away from the simplicity of what signal-store gives us. Signals in general is a big step towards simplicity (computed is easier than dealing with rxjs pipes), and signal-store is already a great tool for organizing state with signals. With the reactivity we get from signals, I guess the question is, do we really need these events? |
💯. Or, slightly rephrased: Do we need to put redux-alike impl. everywhere? (given that it is already implemented in so many places) |
Events are really needed sometimes, but they have a cost. Similar to
This is a list of reasons why I believe events should be avoided when possible and direct calls should be preferred. |
I would definitely need it but I would never dispatch anything from the outside but instead, from within a feature that would trigger an effect on the parent like:
Would that be a valid use case or am I missing something (pretty new to stores tbh)? I tried with an effect in the onInit hook but it went into an infinite loop even though the only dependency there was the selectedCategory EDIT: I could actually use the dispatcher outside of a store/feature in very specific/rare cases but would clearly avoid it as much as possible |
@LcsGa , The same applies to pagination. Then, in the component that displays the items, I would use inputs bound to query params, so they would change on navigation and load items using those params. But I might have misunderstood your case. |
Yep, that could be a way of handling this but it doesn't fit my use case:
|
@LcsGa component with the grid will not be destroyed/reloaded if you navigate to the same route. There are other ways, of course:
|
Yes I completely agree with you but unfortunately I can't do that due to my pagination, as it's made rn. If I had a paginator, I'd do that right away (even though in my case + the router solution, I could simply use an |
🤯 this would make the SignalStore the most versatile state management ever. I would emphasize that:
what a team! 💪 |
Really awesome and simple implementation. I would definitely like to give it a try. I like the last bit about scaling up where everything is so neat and concise. |
Hey @markostanimirovic, any news about this package? |
I actually got a similar situation. If I have a pagination feature and I want to load from server on page change. Option 1: What's best here? |
I'd do the second one but until the events package becomes available in the signal-store package, it's not possible. |
Any chance to make it work with not global stores? (with no providedIn root flag) |
In the current PoC Nevertheless, I would first analyze if local dispatching is needed at all. |
That would perfectly fit for stores with multiple instances. My current project would take great advantages of that. |
Technically, this would work. Especially if it comes to several instances it is questionable though if it is really helpful to add the Event API here. You either need to create different Dispatcher Tokens or care about different Injector nodes. Both is not that easy to follow depending on the developer teams knowledge about Angular's Dependency Injection concept. To summerize this, not everything that is technically possible makes sense from an architectural point of view. Most of the time the Event API will make sense for global State Management. |
@rosostolato, if you like to describe your use case in more detail, this could help to analyze whether additional features would be helpful to finalize the Event API. |
Let me explain using my app as an example. In my app, there are multiple user workspaces, and they work kind of like Chrome tabs. Each workspace is like a separate tab, with its own data and its own store instance. So, when a user is working in a workspace, all the data for that workspace stays in its own store, completely separate from the others. I know I could just use one global store for each entity to hold all the data and create selectors to filter out the entities for the current workspace. But trust me, that gets messy fast. It’s so much better to have individual stores where each one only holds the data for its specific workspace. This way, data from one workspace won’t interact with or accidentally affect another, and it also makes it much easier to create effects that only interact with the stores within the same workspace. All I would need to do is provide the dispatcher and the stores in my workspace tab component @Component({
selector: 'app-workspace-tab',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
Dispatcher
DataStore,
WorkspaceStore,
... other stores
],
imports: [
... rest of the component |
Which @ngrx/* package(s) are relevant/related to the feature request?
signals
Information
This RFC proposes adding the
events
plugin to the@ngrx/signals
package to enable event-based state management with NgRx SignalStore.Key Principles
Prototype
The prototype of the
@ngrx/signals/events
plugin with a demo application is available at the following link: https://github.com/markostanimirovic/ngrx-signals-events-prototypeWalkthrough
Defining Events
Event creators are defined using the
eventGroup
function:Performing State Changes
The reducer is added to the SignalStore using the
withReducer
feature. Case reducers are defined using thewhen
function:Performing Side Effects
Side effects are added to the SignalStore using the
withEffects
feature:Dispatched events can be listened to using the
Events
service.If an effect returns a new event, it will be dispatched automatically.
Reading State
State and computed signals are accessed via store instance:
Dispatching Events
Events are dispatched using the
Dispatcher
service:It's also possible to define self-dispatching events using the
injectDispatch
function:Scaling Up
The reducer can be moved to a separate file using the custom SignalStore feature:
The same can be done for effects:
The final SignalStore implementation will look like this:
Describe any alternatives/workarounds you're currently using
No response
I would be willing to submit a PR to fix this issue
The text was updated successfully, but these errors were encountered: