My Developer Experience with NGXS

As an Angular developer, I have been working with NgRx for a while. However, recently I keep hearing about this state management NGXS. I heard many good things about this library and one pro of this library is that it has a better “developer experience”. As a developer, I want to try it out to see what kind of experience this library would give me and see whether it could “convert” me.

Image for post
Image for post
NGXS

I have created this sample project using NGXS:

It is a simple web app that allows you to search a superhero by name, race, and gender: https://xiongemi.github.io/superhero-search-engine/.

Image for post
Image for post
Sample app developed using NGXS: Superhero Search Engine

Before I Started…

I checked npm trends for these 2 libraries. NgRx is still dominant at the time I wrote this blog (March 2020).

Image for post
Image for post
npm downloads @ngrx/store vs @ngxs/store

There is definitely a bigger community around NgRx than NGXS.

Less Boilerplate?

I read a few blogs online, and most of them mentioned that NGXS has less boilerplate. I was wondering, since both libraries are inspired by Redux, how would one has less boilerplate than the other. When I started using it following the documentation, I finally get what it meant.

So it basically means instead of having 3 files in NGRX (effects, selectors, reducers), it seems to dump everything into one file (state).

NGXS’s .state = NGRX’s .effects + .reducers + .selectors

What I like: .effects + .reducers are together. I could change the state directly inside an asynchronous callback. I don’t need to fire another action in an effect then modify the state in the reducer.

For example, I need to call an API to get all superheroes and put it in the state, in NgRx, I might need 3 actions:

  1. GetSuperheros to call API.
  2. GetSuperherosSuccess when API returns a valid response and I need to update the state with this new response.
  3. GetSuperherosError when API throws an error and I need to handle this error.

In NGXS, I only need 1 action GetSuperheros, and the state file would look like this:

@Action(GetSuperheros)
getHeros(ctx: StateContext<Hero[]>) {
return this.herosService.getHeros().pipe(
tap(heros => {
// equivalent to GetSuperherosSuccess
ctx.setState(heros);
}),
catchError(() => {
// equivalent to GetSuperherosError
return ctx.dispatch(new HandleApiError(new GetSuperheros()));
})
);
}

What I don’t like: In the NGXS’s document, it uses @Select syntax in the components or puts selectors as static methods of state class. I still prefer to put selectors as pure functions in a separate .selectors file. It is easier for me to unit test.

Selectors

Talking about the selectors, NGXS has a build-in function createSelector wherein NgRx, I need to install the 3rd-party reselect library.

NGXS has more built-in plugins than NgRx, such as forms and router.

Forms

To sync the form values with the state, for NgRx, I used the 3rd party library ngrx-forms before. Here is a blog about how to use this library: https://rangle.io/blog/ngrx-forms/. NGXS has its own built-in plugin.

These 2 libraries work very differently and are hard to compare. NGXS’s form plugin still uses Angular’s reactive form; however, ngrx-forms However, if I have a reactive form that is already working, and I need to sync the value and status with the state, NGXS requires less effort.

Plugins

Below is the plugins/libraries for NgRx vs NGXS. So for all the things I could do in NgRx, I could find something equivalent in NGXS.

Router

Devtools

Logger

Storage

Unit Testing

Testing asynchronous actions in .state files

Since NGXS’s .state file is not made of pure functions, it is a bit tricky to test asynchronous actions. In NGXS’s documentation, there seems to be a lot of setup for unit testing.

I did not follow the documentation that uses Testbed, but rather I mocked up the StateContext:

export class MockStateContext {
getState = jest.fn();
patchState = jest.fn();
setState = jest.fn();
dispatch = jest.fn();
}

I used jest as my unit testing framework. If you use jasmine, you could use spyOn to mock up these functions.

Below is an example of a unit test in my sample project. It checks stateContext.setState to be called with the expected new state:

describe('Heros State', () => {
let herosService: MockHerosService;
let herosState: HerosState;
let stateContext: MockStateContext;
beforeEach(() => {
stateContext = new MockStateContext();
herosService = new MockHerosService();
herosState = new HerosState(herosService as any);
});
it('should set heros if it does not exist', done => {
const heros = [mockMaleHumanHero, mockOtherNonHumanNeutral];
stateContext.getState.mockReturnValue([]);
herosService.getHeros.mockReturnValue(of(heros));
herosState.getHeros(stateContext as any).subscribe(() => {
expect(stateContext.setState).toHaveBeenCalledWith(heros);
done();
});
});
});

I find unit testing with the state files is not that difficult, similar to unit testing the .effects files in NgRx.

For .selectors files, since it is made of pure functions, it is pretty simple to unit tests.

Documentation

NgRx definitely has better documentation. I really like the api page of NgRx: https://ngrx.io/api.

I was trying to find the return type of Store.dispatch function, in typing file, it says Observable<any>, but I really want to know what the any type is. I could not find it in the online documentation, I need to console log, and turns out the return is undefined. I do wish this library has a quick API search documentation.

RxJS?

I have read some blogs mentioned that by using NGXS, developers don’t need to be very familiar with RxJS. For my sample app, I only used operatorstap and catchError. However, as an Angular developer, I don’t think we could bypass RxJS. I think developers still need to be familiar with RxJS regardless.

Summary

This is just my personal 2-cent about NGXS as a developer.

Converted? Yes.

2 reasons: forms and fewer actions created.

If a future project requires a lot of form handling or it already uses reactive forms, I would definitely use NGXS.

It also requires fewer actions to be created, so less the code, merrier the developers.

There are always new libraries that come out every year. Even though NGXS is a new library, but it is still “Redux inspired”. As long as developers understand Redux, it is not hard to pick it up.

Happy coding and happy state managing.

A frontend web developer in Toronto

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store