Announcing NGXS 3.4 — Immutability helpers, HMR, SSR & much more
NGXS v3.4 has been a long time coming. It is the result of months of hard work by the team and a steady focus on what will enhance the core of the library without compromising the simplicity that it offers.
Overview
- 🚀 State Operators
- 📶 New Actions Stream Pipe — onActionCompleted
- ♻️ HMR Support
- 🎨 SSR Support
- 💠 New State Lifecycle Event — ngxsAfterBootstrap
- ✂️ Decoupling from Zones.js
- ⏫ Inheritance of State Options
- 🔌 Plugin Improvements
- 🐛 Bug fixes
- 📃 Versioned Docs
- 💥 New Labs Projects
State Operators
State Operators are a mechanism for expressing immutability updates in a declarative, type safe and code completion friendly way. This is a really exciting addition to NGXS. We have enabled a very simple mechanism in `StateContext.setState` to allow for an abstraction of the calculation of the new state to be passed to the `setState` call. This abstraction takes the form of a function that returns the new state given the existing state. This function is referred to as a State Operator and has the following signature: <T>(existing: Readonly<T>) => T
We have included some common operators in NGXS to get you started but the real power lies in the ability for you to create your own operators to express the updates to your domain cleanly. See this page in our docs for more information: https://ngxs.gitbook.io/ngxs/advanced/operators
After leveraging this approach to make your own State Operators your @Action
handler code could look like this:
Here the developer has defined their own addEntity
State Operator that handles the updating of the entities
and ids
parts of their state.
A more in depth look at State Operators can be found in the following medium post:
New Actions Stream Pipe — onActionCompleted
Action Handlers are provided in NGXS to be able to observe the Actions
stream and respond to the lifecycle events of actions. Before NGXS v3.4 the following pipes exist for filtering the Actions
observable:
ofAction
any lifecycle event happens for the specified actionofActionDispatched
when the specified action has been dispatchedofActionSuccessful
when the specified action has completed successfullyofActionCanceled
when the specified action has been canceledofActionErrored
when the specified action has errored
Each of these pipes filter the Actions
stream and provide the instance of the action as the resultant value in the observable. This is great but there are three pipes that represent the different possible completion outcomes, and the ofActionErrored
returns the action and no information about the error (this will be fixed in the next major version).
In v3.4 we introduce a new pipe called ofActionCompleted
. This new pipe handles all completion outcomes and returns a summary of the action completion in the following form:
interface ActionCompletion<T = any, E = Error> {
action: T;
result: {
successful: boolean;
canceled: boolean;
error?: E;
};
}
This new pipe is very useful and solves the problem of determining the error that caused an action to fail (see issue #518 ).
HMR Support
We have added a plugin called @ngxs/hmr-plugin
which handles the details of getting hot module reloading working with NGXS so that the state is preserved between module reloads.
There is an extensive tutorial available here:
Details of this plugin are found in our docs here:
SSR Support
We have adjusted the way that NGXS interacts with zone.js so that you can now use Server Side Rendering with your NGXS app and everything just works. No special effort required! …other than the usual Angular Universal setup process found here: https://angular.io/guide/universal
New State Lifecycle Event — ngxsAfterBootstrap
There have been some use cases where the NgxsOnInit
lifecyle hook has not been sufficient because it runs before the Angular APP_INITIALISER
lifecyle event. In v3.4 your NGXS States are now able to respond to an ngxsAfterBootstrap
lifecycle event. This event is triggered when the application is fully rendered.
In order to make use of this lifecycle event you need to implement the NgxsAfterBootstrap
interface in a State class and provide an implementation for the ngxsAfterBootstrap
method exposed by this interface. This method will be invoked after the root component and all its children components are successfully bootstrapped.
See our docs here for more details:
https://ngxs.gitbook.io/ngxs/advanced/life-cycle#ngxsafterbootstrap
Decoupling from Zones.js
As an angular library NGXS has had to do some optimisations regarding Angular’s zone API when it handles actions. By default methods decorated with `@Action` decorator are called outside Angular’s zone which gives some performance benefits for the average usage scenarios. We have moved all logic related to the exection context under which NGXS executes into an execution strategy. This allows for an advanced user to customise the way that code is executed to optimise for their specific use case. For example, an application that uses OnPush
change detection everywhere can simply provide the NoopNgxsExecutionStrategy
so that there is no interaction with zones at all.
Another benefit of this approach is that zone.js is now isolated into a single point in the codebase which fits quite nicely for applications that do not use zones (when using the new Ivy renderer for example) and therefore would not need to take on this dependency.
More details can be found here: https://ngxs.gitbook.io/ngxs/advanced/options
Inheritance of State Options
Meta properties of a @State
decorator can now be inherited from its super class (if present).
In the example above the state “b” will inherit the same default as the state “a”. Note: The only properties that aren’t inherited are the children and name properties.
Plugin Improvements
Our plugins have also recieved some love:
- Feature: WebSocket Plugin — Add
WebSocketDisconnected
action to notify of disconnection #825 - Fix: Websocket Plugin — server/network error triggered close should dispatch
WebSocketDisconnected
#832 - Fix: WebSocket Plugin —
WebsocketMessageError
should notify of errors #825 - Fix: Logger Plugin — Log group not closed on error #831
- Fix: Form Plugin — correct state synchronization with dirty flag #862
Bug fixes and other features
Versioned Docs
Our docs have recieved a large number of contributions from our enthusiastic community and they are more thorough than ever before. We have also enabled a mechanism for versioning of our docs so that we can showcase new features in the development version of our docs without confusing users that are still on a stable version of the library. Older version of the library also have versions of the docs that align to their feature sets.
New Labs Projects
Async Storage Plugin
A community member has created the @ngxs-labs/async-storage-plugin
which is a variant of the normal storage plugin but expects the storage interface to be asynchronous. This plugin enables the connection of various asynchronous storage implementations, opening up some use cases that were not possible with the standard synchronous plugin intended for in-browser storage.
The primary example of this plugin provides an implementation that makes use of Ionic’s storage service for storage. See the readme for more information: https://github.com/ngxs-labs/async-storage-plugin