Announcing NGXS 3.5

Mark Whitfeld
NGXS
Published in
7 min readJul 23, 2019
Photo by Vishnu R Nair on Unsplash

NGXS v3.5 has been a long-awaited release. It is the result of months of hard work by the team, an unwavering commitment to ensuring the backward compatibility of the library and a steady focus on what will enhance the core of the library without compromising the simplicity that it offers.

Overview

  • 🚀 Angular 8 Support (officially)
  • 🎨 Selector Options
  • 🔌 Plugin Improvements

Angular 8 Support

NGXS 3.4 works perfectly with Angular 8 but now as of 3.5 we officially support this latest version of Angular. It has been a long road to get to this official support mainly due to the fact that the related upgrade to TypeScript caused issues for the backward compatibility of the library.

One of our major concerns has been to honor the support of previous versions of Angular. The type definitions produced by TypeScript 3.4 (which is a requirement of the Angular 8 compiler) were unfortunately not backward compatible to previous versions of Typescript. After a lengthy wrestle with the intricacies of TypeScript we eventually had to change our build process to generate the type definitons using TypeScript 3.1 but still build the javascript output using the Angular 8 compiler. This approach satisfied our rigorous testing and we are confident that we have full support for this new version of Angular.

The other challenge that we faced was with the strictness of the typings for this new version of TypeScript. In particular, many of the standard state operators in NGXS take advantage of certain aspects of the type inference in TypeScript to achieve their type safety magic. The new version of TypeScript introduces the unknown type where previously the {} type would allow for the looser type inference. After improving many aspects of the typing of the operators we had to concede just short of the ideal TypeScript 3.4 types for the operators due to the risk of breaking compatibility with previous versions of TypeScript.

Important Note
Due to the challenges stated above your project may have a small chance of having type inference issues after the upgrade to TypeScript 3.4 that comes along with the Angular upgrade. These “breaking changes” are caused by changes to TypeScript and not due to changes in NGXS.
For example, if you had some implicit typing for a patch operator like this:

patch({
users: updateItem(
user => user.id === 123,
patch({ name: ‘Mark’ })
)
})

TypeScript may complain about the type inference and you would, therefore, need to be more specific about your types (which is good practice anyway):

patch({
users: updateItem<User>(
user => user.id === 123,
patch({ name: ‘Mark’ })
)
})

We are confident that we have done everything possible from our side to soften the pain of the TypeScript upgrade.

Selector Options

In NGXS 3.5 we introduce the ability to provide selector options at a global, state or individual selector level. The two options that have been exposed are:

  • supressErrors: This configures if the selectors should suppress internal errors or not. Default: true
  • injectContainerState: This configures the default behavior for how selectors defined within a state class are joined to other selectors. If this value is true then the container state will be injected as the first parameter of the method, if it is false then only the provided selectors will be provided as parameters to the joined selector. See the section in the docs on joining selectors for more details on this setting. Default: true

Both of these options are set to change their defaults in the v4 release of NGXS in order to make the debugging of selectors clearer (supressErrors: false) and to optimize the memoization of joined selectors (injectContainerState: false).

Through these selector option settings developers will be able to:

  • Take advantage of NGXS v4 behavior before it arrives.
  • Soften the impact of the changed defaults in NGXS v4 when it arrives by setting these back to the v3 defaults and then upgrading the behavior in a piecemeal fashion.

The selector options can be provided in the selectorOptions property of the global options passed to the NgxsModule.forRoot call (see module options) or through the @SelectorOptions decorator at a Class or Method level in order to configure the behavior of selectors within a particular scope.

See the selector options section of the docs for more information.

Plugin Improvements and Fixes

Router Plugin

  • Added a new RouterDataResolved action for the @ngxs/router-plugin.
    The RouterNavigation action is fired for every route change but unfortunately it triggers before the router resolves the data related to the route (there is no later stage hook available in angular that is consistently triggered). The new `RouterDataResolved` action allows us to now respond to changes to the route’s data. It is dispatched every time the router resolves data related to a route (as long as there are resolvers attached to the route). This is used internally to ensure that the state is updated with the latest resolved data but can also be subscribed to from the Actions stream for your own routing needs.
  • Fixed router plugin issue where a change to the query params didn’t result in the `RouterNavigation` action being dispatched.
  • Fixed a router plugin issue when used in conjunction with the storage plugin where the user couldn’t navigate to a manually entered URL because the previous URL in the storage would cause a redirect.

Forms Plugin

  • Introduced conditional debounce in the NGXS form plugin.

HMR Plugin

  • HMR plugin now removes old styles after reloading.

Logger Plugin

  • The logger plugin is now compatible with IE11 as we removed unsupported `Object.entries`.

Storage Plugin

  • Fixed an issue where the use of the storage plugin broke SSR.

Websocket Plugin

  • Added a new action called WebSocketConnectionUpdated which is dispatched only in cases when there is an existing connection and the ConnectWebSocket action is dispatched to open a new one. The old connection would then be closed, the WebSocketConnectionUpdated action dispatched and the new connection opened. The WebSocketDisconnected action would not be fired in this case because the plugin has not lost connection but merely changed its’ connection.
  • Fixed an issue where dispatching the WebSocketDisconnected action caused the socket to not be able to subsequently reconnect and receive messages.

Other Bug Fixes

  • Fixed issue related to the `ngxsOnInit` lifecycle method not being invoked in root states under certain scenarios.

New NGXS Labs Projects

@ngxs-labs/select-snapshot

This plugin provides a @SelectSnapshot decorator that works in the same way as the standard @Select decorator with one main difference… you can access the state without having to go through an observable. It conveniently overrides the getter for the property which returns the latest snapshot from the state.

Here is a simple example leveraging this new decorator:

Note: it is advisable to change the execution strategy for NGXS to the NoopNgxsExecutionStrategy in the root module options when using this plugin. This is so that all modifications to the state happens within the angular zone and trigger the components to refresh with the latest value from this property. See the docs here for more information.

Also, due to the fact that there is no observable to trigger change detection, this decorator should be used carefully when theChangeDetectionStrategy of a component has been set to ChangeDetectionStrategy.OnPush.

A full write up on this plugin is found here:

And the repo is found here:

@ngxs-labs/immer-adapter (revised approach)

Add `ImmutableSelector`, `ImmutableContext` decorators for easier and safer immutable state updates using Immer.

When you have more deeply nested state it becomes harder to manage aspects of immutability:

The @ngxs-labs/immer-adapter allows you to express this in a much cleaner way by leveraging the power of Immer’s proxies:

While the setState usage above could also be expressed using state operators, some developers prefer the more imperative style of mutating objects directly (especially when the state is deeply nested). Immer provides the bridge to allow for these mutable style updates without compromising the immutability of the state.

The @ImmutableSelector decorator allows the developer to mutate the state within a selector and return the modified data without worrying about making changes to the underlying immutable state. This is especially helpful to avoid mistakes like using an array’s sort() method, which would modify the underlying array.

The repo for this plugin is found here:

Ivanov Max (splincode) has done a great writeup on how this plugin can assist with immutability in the third part of his blog series on the topic:

Some Useful Links

If you would like any further information on changes in this release please feel free to have a look at our change log. The code for NGXS is all available at https://github.com/ngxs/store and our docs are available at http://ngxs.io/. We have a thriving community on our slack channel so come and join us to keep abreast with the latest developments. Here is the slack invitation link: https://now-examples-slackin-eqzjxuxoem.now.sh/

--

--

Mark Whitfeld
NGXS
Editor for

A Software Developer passionate about writing clean, maintainable, high quality software that delights the user. #TDD #CleanCode #SoftwareCraftsmanship