Deep Comparison Of State Management Solutions for Angular

Vytautas Pranskunas
7 min readApr 25, 2019

--

Nowadays, when everything moves towards SPA (single page application) and PWA (progressive web application) more and more developers turns to Angular, React, Vue or any other javascript libraries / frameworks. For small / medium apps we do not necessary need state management, but with growing demand for functionality from business, applications are getting bigger and bigger then we have to think about it.

In this topic I am going to analyse 3 state management solutions

  • @ng-state/store
  • ngrx/store
  • Akita

Let’s begin 🔥 😎

Descriptions

@ng-state/store

RxJS and Immutable.js powered nested state management for Angular 2 applications inspired by @ngrx/store. It is simple, fast, reliable with no boilerplate.

ngrx/store

Store is RxJS powered state management for Angular applications, inspired by Redux. Store is a controlled state container designed to help write performant, consistent applications on top of Angular.

Akita

Whether it be Angular, React, Vue, Web Components or plain old vanilla JS, Akita can do the heavy lifting and serve as a useful tool for maintaining clean, boilerplate-free, and scalable applications.

State access

In most rxjs based solutions there is one and only way to access state - subscription to observable. This might not be very big problem, especially when you use Angular pipes, but might introduce many boilerplate once you need to use some values from your state in non reactive actions e.g. you want to use some value in your http request or pass that value to another function for calculations

ngrx - As you can see does not support it at all.

@ng-state/store - supports it via this.state variable in actions

akita - supports it partially. Partially means that due to weird/mixed state nature it supports non observable state access only for arrays. I will talk about it later in Stores and Architecture section.

State Nesting

Coming from Redux ideology there should not be nested state. However, you will face many boilerplate (like state normalization etc) when storing flat state. This mostly done due to difficulty of updating deep nested states. You can read more about Redux difficulties here.

ngrx and akita allows state nesting but facing same difficulties when updating. Deeper state - more difficult to update.

@ng-state/store does not suffer from this because of Immutable.js + cursor which allows to work with specific peace of state.

Performance

It is very important that state management solution would not become bottleneck. It might not be noticeable under low loads or under not intensive data change features (like live charts or etc), but it should be kept in mind when choosing state management solution.

I have created application with all three candidates with flowing scenario:

  • add 1000 rows to UI per 10 iterations: 1000 operations
  • Update 1000 columns 50 times: 50 000 operations

Results were not so surprising because before starting this test I was a bit skeptical about Akita’s custom operators. Custom operators is almost often slower than native javascript. Why almost? Because Immutable.js is an exception.

Akita’s update took 48s. More than that - Akita died at all when columns count was increased up to 10 000. However @ng-state/store and ngrx handled it almost identically in about ~7.2s. Maybe I have done something wrong, like not Akita’s way but that was the most obvious way that worked for others.

It is worth to notice that ngrx did plain operations while @ng-state/store was performing batch updates. Akita however does not support batch updates.

@ng-state/store

ngrx

Akita

You can see it live here. Open dev tools console to see results.

UPDATE: I was able to make Akita handle all 50000 operations by adding sampleTime to query but anyway it took 50s and UI was not runing so smooth.

Also I removed 100ms delay for @ng-state/store and ngrx because they are able to run smooth without it either. With this time, that needs for updating 50k items, dropped to 5 and 4 seconds accordingly.

Other Features

Tooling

we can see that all libraries supports almost every tool. @ng-state/store does not have schematics support but keeping in mind that it need only one file to add (actions) it is perfectly covered by cli.

Boilerplate

I am fan of KISS principle so I am against boilerplate. However there are few topics out-there which thinks opposite.

From my perspective boilerplate seems not a big problem when everything is done following agreed rules especially in greenfield projects where every developer is dedicated to do things right.

But usually it is the case until

  • project grows
  • project moved to v2+ 😅
  • core developers are moved to another project and less experienced stay
  • when we need quick fix
  • or just fun is over and we need to deal with boilerplate forever 😜

As we can see @ng-state/store adds no boilerplate at all! Just 1 actions file.

Unit testing

Unit testing is important part of every application so it is important that we can configure and use mocked state in our unit tests.

Stores and Architecture

@ng-state/store is single store solution. It treats state as single source of truth. It does not create any special structure or wrap your state into it. So you are free to use any state structure you want. This makes it predictable and easy to use.

However multistore solution can be achieved by registering store module in different lazy loaded route modules.

Because it connects certain peaces of state with components, your components really becomes representatives of app state which leads to cleaner and well designed architecture.

ngrx is single store / state solution. Multiple stores can be achieved by using StoreModule.forFeature(‘featureName’, reducers). Though it isn’t the same thing as having two separate stores it is the same for most practical purposes. Same as @ng-state/store, ngrx does not create any special structure or wrap your state into it. So you are free to use any state structure you want. This makes it predictable and easy to use.

Akita is multi-store solution. You can create as much stores as you want. From one side it is handy, because you can inject any store and use any query in any component, but from other side it does not enforce you to think about correctly separated logic peaces inside your state tree which might lead to troubles and mess.

Akita also wraps your state to some prepared structure, which from first glance designed for CRUD applications and your custom state it is just for UI representations like filters etc: in image below ui is default state.

Most of API, made for store and queries, are designed for manipulating with entities array but not with your state.

From my perspective it removes flexibility, adds confusion and reduces cases where it can be used.

Debugging

@ng-state/store provides various ways to debug current state

  • Redux DevTools - whole state or part of it can be tracked
  • Console state tracking - whole state or part of it can be tracked
  • Console current state view
  • Console history view

ngrx and Akita provides integration with Redux DevTools.

Learning complexity

  • @ng-state/store - Medium
  • ngrx - High
  • Akita - Low

@ng-state/store is not complex by design. The only thing why it was evaluated as medium because it takes time to learn Immutable.js.

ngrx is way more complex. I mean, it might be simple but when application grows you need to be master avoid all Redux downsides like actions hell and etc.

Akita learning curve is as low because you need time to learn its API which is not too big or too complex.

Community

From the community perspective (based on google trends, github stars, contributors and npm downloads) it is obvious that ngrx comes first because it was first implementation of Redux ideology in Angular. Second is Akita and ng-state takes third place.

Size

Summing all dependencies together, production size is almost similar for all three packages.

Which to choose?

This is an answer most developers are looking for 😈 But there is no one answer. If it would be, there would be only one solution 😆

But anyway, we need to make a choice somehow. So I would follow these guidelines:

  • If you are coming from Redux world and see benefits of using it - you probably should go with ngrx
  • If you are building some CRUD and data not intensive application - you might consider Akita
  • If you not totally satisfied with Redux way - you probably need to try @ng-state/store

If you enjoy the article, give some claps to motivate ✋✋✋

Follow me on twitter @VPranskunas 👍

--

--