Contribute to help us improve!
Are there edge cases or problems that we didn't consider? Is there a technical pitfall that we should add? Did we miss a comma in a sentence?
If you have any input for us, we would love to hear from you and appreciate every contribution. Our goal is to learn from projects for projects such that nobody has to reinvent the wheel.
Let's collect our experiences together to make room to explore the novel!
To contribute click on Contribute to this page on the toolbar.
Introduction to NgRx
The need for client side state management
You may wonder why you should bother with state management. Usually data resides in a back-end storage system, e.g. a database, and is retrieved by the client on a per-need basis. To add, update, or delete entities from this store, clients have to invoke API endpoints at the back-end. Mimicking database-like transactions on the client side may seem redundant. However, there are many use cases for which a global client-side state is appropriate:
-
the client has some kind of global state which should survive the destruction of a component, but does not warrant server side persistence, for example: volume level of media, expansion status of menus
-
sever side data should not be retrieved every time it is needed, either because multiple components consume it, or because it should be cached, e.g. the personal watchlist in an online streaming app
-
the app provides a rich experience with offline functionality, e.g. a native app built with Ionic
Saving global states inside the services they originates from results in a data flow that is hard to follow and state becoming inconsistent due to unordered state mutations. Following the single source of truth principle, there should be a central location holding all your application’s state, just like a server side database does. State management libraries for Angular provide tools for storing, retrieving, and updating client-side state.
Why NgRx?
As stated in the introduction, devon4ng does not stipulate a particular state library, or require using one at all. However, NgRx has proven to be a robust, mature solution for this task, with good tooling and 3rd-party library support. Albeit introducing a level of indirection that requires additional effort even for simple features, the Redux concept enforces a clear separation of concerns leading to a cleaner architecture.
Nonetheless, you should always compare different approaches to state management and pick the best one suiting your use case. Here’s a (non-exhaustive) list of competing state management libraries:
-
Plain RxJS using the simple store described in Abstract Class Store
-
NgXS reduces some boilerplate of NgRx by leveraging the power of decorators and moving side effects to the store
-
MobX follows a more imperative approach in contrast to the functional Redux pattern
-
Akita also uses an imperative approach with direct setters in the store, but keeps the concept of immutable state transitions
Setup
To get a quick start, use the provided template for devon4ng + NgRx.
To manually install the core store package together with a set of useful extensions:
NPM:
`npm install @ngrx/store @ngrx/effects @ngrx/entity @ngrx/store-devtools --save`
Yarn:
`yarn add @ngrx/store @ngrx/effects @ngrx/entity @ngrx/store-devtools`
We recommend to add the NgRx schematics to your project so you can create code artifacts from the command line:
NPM:
`npm install @ngrx/schematics --save-dev`
Yarn:
`yarn add @ngrx/schematics --dev`
Afterwards, make NgRx your default schematics provider, so you don’t have to type the qualified package name every time:
`ng config cli.defaultCollection @ngrx/schematics`
If you have custom settings for Angular schematics, you have to configure them as described here.
Concept
Figure 1 gives an overview of the NgRx data flow. The single source of truth is managed as an immutable state object by the store. Components dispatch actions to trigger state changes. Actions are handed over to reducers, which take the current state and action data to compute the next state. Actions are also consumed by-effects, which perform side-effects such as retrieving data from the back-end, and may dispatch new actions as a result. Components subscribe to state changes using selectors.
Continue with Creating a Simple Store.