Angular 6 - Why use @ngrx/store rather than service injection
Asked Answered
A

6

157

I am recently learning Angular 6 with @ngrx/store while one of the tutorial is to use @ngrx/store for state management, however I don't understand the benefit of using @ngrx/store behind the scene.

For example, for a simple login and signup action, previously by using the service(Let's call it AuthService) we might use it to call the backend api, store "userInfo" or "token" in the AuthService, redirect user to "HOME" page and we can inject AuthService in any component where we need to get the userInfo by using DI, which simply that one file AuthService handles everything.

Now if we are using @ngrx/store, we need to define the Action/State/Reducer/Effects/Selector which probably need to write in 4 or 5 files to handle above action or event, then sometimes still we need to call backend api using service, which seems much much more complicated and redundant...

In some other scenario, I even see some page uses @ngrx/store to store the object or list of object like grid data., is that for some kind of in-memory store usage?

So back to the question, why are we using @ngrx/store over service registration store here in Angular project? I know it's for "STATE MANAGEMENT" usage, but what exactly is the "STATE MANAGEMENT"? Is that something like transaction log and When do we need it? Why would we manage it on the front end? Please feel free to share your suggestion or experience in the @ngrx/store area!

Arta answered 10/7, 2018 at 21:47 Comment(3)
Last year I started a new job in one company. They were using Angular with Redux. I haven't touched Redux, but I've been developing in Angular ever since its beta release. My first impression was what the hell is this? So much complication just to communicate with API and subscribe to that data. They literally used Redux for everything! It was such a mess that it was impossible to work. There is really no need to integrate Redux / Ngrx to an Angular app. You can do everything the 'Angular way'Pindus
NgRx exponentially increases the code complexity with a hell lot of unnecessary boilerplate code. On the other hand, it hardly offers anything beyond what Angular, as a complete framework, has already offered out of the box. This blog post covers all the information you need: Angular Application State Management: You Do (Not) Need External Data StoresFarinaceous
Sorry guys I disagree. When your project gets to a certain size you start to get issues keeping everting updated. I was using a bunch of BehavoirSubjects and the increase in complexity meant that I was re-inventing NGRX but badly. I re-wrote my app using NGRX and I have access to the store everywhere. The V8 syntax changes reduced the amount of boilerplate too. My project is cleaner, easier to read and faster to execute.Archenteron
P
55

I think you should read those two posts about Ngrx store:

If the first one explains the main issues solved by Ngrx Store, it also quote this statement from the React How-To "that seems to apply equally to original Flux, Redux, Ngrx Store or any store solution in general":

You’ll know when you need Flux. If you aren’t sure if you need it, you don’t need it.

To me Ngrx store solves multiple issues. For example when you have to deal with observables and when responsability for some observable data is shared between different components. In this case store actions and reducer ensure that data modifications will always be performed "the right way".

It also provides a reliable solution for http requests caching. You will be able to store the requests and their responses, so that you could verify that the request you're making has not a stored response yet.

The second post is about what made such solutions appear in the React world with Facebook's unread message counter issue.

Concerning your solution of storing non-obvervable data in services. It works fine when you're dealing with constant data. But when several components will have to update this data you will probably encounter change detection issues and improper update issues, that you could solve with:

  • observer pattern with private Subject public Observable and next function
  • Ngrx Store
Playbill answered 12/7, 2018 at 8:10 Comment(1)
Everything you mentioned as a benefit for NGRX could still be done using a singleton service.Fluorescence
C
42

I'm almost only reading about the benefits of Ngrx and other Redux like store libraries, while the (in my opinion) costly tradeoffs seem to be brushed off with far too much ease. This is often the only reason that I see given: "The only reason not to use Ngrx is if your app is small and simple". This (I would say) is simply incomplete reasoning and not good enough.

Here are my complaints about Ngrx:

  • You have logic split out into several different files, which makes the code hard to read and understand. This goes against basic code cohesion and locality principles. Having to jump around to different places to read how an operation is performed is mentally taxing and can lead to cognitive overload and exhaustion.
  • With Ngrx you have to write a lot more code, which increases the chances of bugs. More code -> more places for bugs to appear.
  • An Ngrx store can become a dumping ground for all things, with no rhyme or reason. It can become a global hodge podge of stuff that no one can get a coherent overview of. It can grow and grow until no one understands it any more.
  • I've seen a lot of unnecessary deep object cloning in Ngrx apps, which has caused real performance issues. A particular app I was assigned to work on was taking 40 ms to persist data in the store because of deep cloning of a huge store object. This is over two lost render frames if you are trying to hit a smooth 60 fps. Every interaction felt janky because of it.
  • Most things that Ngrx does can be done much simpler using a basic service/facade pattern that expose observables from rxjs subjects.

Just put methods on services/facades that return observables - such a method replaces the reducer, store, and selector from Ngrx. And then put other methods on the service/facade to trigger data to be pushed on these observables - these methods replace your actions and effects from Ngrx. So instead of reducers+stores+selectors you have methods that return observables. Instead of actions+effects you have methods that produce data the the observables. Where the data comes from is up to you, you can fetch something or compute something, and then just call subject.next() with the data you want to push.

  • The rxjs knowledge you need in order to use ngrx will already cause you to be competent in using bare rxjs yourself anyways.
  • If you have several components that depend on some common data, then you still don't need ngrx, as the basic service/facade pattern explicitly handles this already.
  • If several services depend on common data between them, then you just make a common service between these services. You still don't need ngrx. It's services all the way down, just like it is components all the way down.

For me Ngrx doesn't look so good on the bottom line.

It is essentially a bloated and over engineered Enterprise™🏢👨‍💼🤮 grade Rxjs Subject, when you could have just used the good old and trusty Rxjs Subject. Listen to me kids, life is too short for unnecessary complexity. Stick to the bare necessities. The simple bare necessities. Forget about your worries and your strife.

Creasy answered 3/2, 2021 at 10:24 Comment(6)
I agree with this. The reasoning I see for NgRx is very similar to that of why use RxJs. The answer is always "because you can stream values with Observables". Really? That's not a great reason to dump Promise all-together -- especially considering the built-in language support async/await which make your code so much easier to read and understand than all the mind bending callbacks you have to use in RxJs.Petrify
Well if you need to stream data, then a promise won't work, because it can only emit once. In that case you need an observable library like rxjs, because you don't want go get stuck in callback hell either, that's for sure.Concierge
I have never built an app that needed to stream data. All of my business apps need to call an api and get one response back. And to use RxJs just because the 1 app I might build 10 years from now might need streaming, is ridiculous.Petrify
I only work on apps that get live data pushed/streamed over a websocket. So I end up mostly using rxjs for that.Concierge
that's if you're communicating with api/client yes then you would only need a promise action. but if. let's say you are communicating from a FE comp that makes that req and you need a constant res on another component, you might wanna use Rxjs. Ngrx like redux in Angular, after sometime, still just boils down to a ton of boilerplate, or solve a really bad architecture. Where. No one has any idea of making a FE app scalable. perhaps that's why I dislike React so much... to confusing for something to simple. dunno my opinion anywayComical
This absolutely hit the nail on the head. The other quote I like from another post (greatwizardofnone.medium.com/…) is the question: "Is this library working for you, or are you working for it?"Americanism
Q
18

I've been working with NgRx for over three years now. I used it on small projects, where it was handy but unnecessary and I used it in applications where it was perfect fit. And meanwhile I had a chance to work on the project which did not use it and I must say it would profit from it.

On the current project I was responsible for designing the architecture of new FE app. I was tasked to completely refactor the existing application which for the same requirements used non NgRx way and it was buggy, difficult to understand and maintain and there was no documentation. I decided to use NgRx there and I did it because of following reasons:

  • The application has more than one actor over the data. Server uses the SSE to push state updates which are independent from user actions.
  • At the application start we load most of available data which are then partially updated with SSE.
  • Various UI elements are enabled/disabled depending on multiple conditions which come from BE and from user decisions.
  • UI has multiple variations. Events from BE can change currently visible UI elements (texts in dialogs) and even user actions might change how UI looks and works (recurring dialog can be replaced by snack if user clicked some button).
  • State of multiple UI elements must be preserved so when user leaves the page and goes back the same content (or updated via SSE) is visible.

As you can see the requirements does not meet the standard CRUD operations web page. Doing it the "Angular" way brought such complexity to the code that it became super hard to maintain and what's worst by the time I joined the team the last two original members were leaving without any documentation of that custom made, non NgRx solution.

Now after the year since refactoring the app to use NgRx I think I can sum up the pros and cons.

Pros:

  • The app is more organized. State representation is easy to read, grouped by purpose or data origin and is simple to extend.
  • We got rid of many factories, facades and abstract classes which lost their purpose. The code is lighter, and components are 'dumber', with less hidden tricks coming from somewhere else.
  • Complicated state calculations are made simple using effects and selectors and most components can be now fully functional just by injecting the store and dispatching the actions or selecting the needed slice of the state while handling multiple actions at once.
  • Because of updated app requirements we were forced to refactor the store already and it was mostly Ctrl + C, Ctrl + V and some renaming.
  • Thanks to Redux Dev Tools it is easier to debug and optimize (yep really)
  • This is most important - even thought our state itself is unique the store management we are using is not. It has support, it has documentation and it's not impossible to find solutions to some difficult problems on the internet.
  • Small perk, NgRx is another technology you can put to your CV :)

Cons:

  • My colleagues were new to the NgRx and it took some time for them to adapt and fully understand it.
  • On some occasions we introduced the issue where some actions were dispatched multiple times and it was difficult to find the cause of it and fix it
  • Our Effects are huge, that's true. They can get messy but that's what we have pull requests for. And if this code wasn't there it would still end up somewhere else :)
  • Biggest issue? Actions are differentiated by their string type. Copy an action, forget to rename it and boom, something different is happening than you expect, and you have no clue why.

As a conclusion I would say that in our case the NgRx was a great choice. It is demanding at first but later everything feels natural and logical. Also, when you check the requirements, you'll notice that this is a special case. I understand the voices against NgRx and in some cases I would support them but not on this project. Could we have done it using 'Angular' way? Of course, it was done this way before, but it was a mess. It was still full of boiler plate code, things happening in different places without obvious reasons and more.

Anyone who would have the chance to compare those two versions would say the NgRx version is better.

Qnp answered 29/4, 2021 at 8:22 Comment(4)
Simply the best answer! Fully agree on all the mentioned things. Have 3 yrs exp with NgRx - already faced all the mentioned pros and cons.Cytoplasm
I agree, I have also worked with NgRx for a couple years and have seen all of these pros and cons. I would add another pro that no one seems to mention, memoized selectors. The ability to use pure functions that are memoized is a huge benefit to NgRx.Neukam
If you liked NGRX, try NGXS, it's just simpler to use with less code :)Nikolia
Did already, I also tried both Akita and Elf.Qnp
H
9

There is also a 3rd option, having data in service and using service directly in html, for instance *ngFor="let item of userService.users". So when you update userService.users in service after add or update action is automatically rendered in html, no need for any observables or events or store.

Haldis answered 3/4, 2019 at 21:10 Comment(2)
It doesn't work in AOT if the service is injected as a private one. The best practice is to not to expose a service to a component's template. Rather, keep a variable in the component and get/set it based on the service's variable.Friable
we have to inject the service as public to expose it to component template.Respecting
T
5

If the data in your app is used in multiple components, then some kind of service to share the data is required. There are many ways to do this.

A moderately complex app will eventually look like a front end back end structure, with the data handling done in services, exposing the data via observables to the components.

At one point you will need to write some kind of api to your data services, how to get data in and out, queries, etc. Lots of rules like immutability of the data, and well defined single paths to modify the data. Not unlike the server backend, but much quicker and responsive than the api calls.

Your api will end up looking like one of the many state management libraries that already exist. They exist to solve difficult problems. You may not need them if your app is simple.

Toadinthehole answered 13/5, 2020 at 20:37 Comment(0)
C
0

NGRX sometimes has a lot of files and a lot of duplicate code. Currently working on a fix for this. To make generic type classes for certain NGRX state management situations that are very common inside an Angular project like pagers and object loading from back-ends

Credential answered 7/12, 2021 at 22:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.