Announcing NGXS 3.6

Mark Whitfeld
NGXS
Published in
9 min readDec 11, 2019
Photo by Ray Hennessy on Unsplash

NGXS v3.6 has been the result of months of hard work by the team, an unwavering commitment to ensuring the stability of the library and a steady focus on what will enhance the core of the library without compromising the simplicity that it offers.

Overview

  • 🌿 Ivy Support
  • 💔 Breaking Changes?
  • 💦 Fixed Actions Stream Subscriptions Leak
  • 🚧 Improved Type Safety
  • ㊗ ️ State Token
  • 💥 New Lifecycle Hook: ngxsOnChanges
  • 🔧 Other Fixes
  • 🔌 Plugin Improvements and Fixes
  • 🔬 NGXS Labs Projects Updates

🌿 Ivy Support

We are actively working on support for Ivy and are 99% there. The @ngxs/store library is fully compatible with Ivy and most of our plugins are compatible. The only plugin that has an issue is the @ngxs/router-plugin. We are working with the Angular team to resolve this issue (see https://github.com/angular/angular/issues/34191).

Due to changes in Angular DI with Ivy, there will be a very small change that you will have to make to your states. This is detailed in the docs here.
To support our users in making this small change we have added a check (in development mode) to warn of incorrect configuration when using Ivy.

If you pick up any issues in testing this version with Ivy please log an issue and we will look into it immediately.

Related PRs: #1278, #1397, #1459, #1469, #1472, #1474

💔 Breaking Changes?

If you are using TypeScript 2.7 and Angular 5 you will need to update to TypeScript 2.8.1 and Angular 6.1.0 at a minimum. We were forced to drop support for these in order to provide support for Ivy. These versions are also no longer supported the Angular team either. This has not resulted in a major version change for NGXS due to the fact that it is not our API that has changed, rather a dependency.

💦 Fixed Actions Stream Subscriptions Leak

We fixed a subtle memory leak that would occur in applications where the Actions stream was used from objects that had a short-lived lifetime (ie. components). This bug would keep these objects in memory through an implicit reference from an undisposed subscription to this.

Related PRs: #1381

🚧 Improved Type Safety

We have added vastly improved type safety for the @Select decorator. If your application fails some type checks after upgrading then we have just saved you some potentially obnoxious runtime bugs 😉.

We have also added some typing sanity checks to what can be passed to the children property of a state.

Related PRs: #1453, #1388

㊗ ️ State Token

We have added a new optional construct called a State Token to NGXS. It is very similar in concept to Injection Tokens in Angular. A State Token can be used as a representation of a state class without referring directly to the state class itself. This allows for a layer of indirection when referring to a state which improves type safety, refactoring and potential for referring to a state before it is lazy loaded.

When creating a StateToken you will provide the location that the state should be stored on your state tree. You can also set a default state model type of the parameterized type T, which can assist with ensuring the type safety of referring to your state in your application. The state token is declared as follows:

const TODOS_STATE_TOKEN = new StateToken<TodoStateModel[]>('todos');

You may choose to not expose the model of your state class to the rest of the application. You can do this by passing a type of `unknown` or `any` to the token. This is useful if you want to keep all knowledge of the structure of your state class model private.

const TODOS_STATE_TOKEN = new StateToken<unknown>('todos');

You can use this token as the name property in your @State declaration. It will be used to provide the path for the state and can also infer the state model type if it has been included in the token. The token can be used in your @State declaration as follows:

A state token with a model type provided can be used in other parts of your application to improve type safety for the @State, @Selector and @Selectdecorators.

The following code demonstrates mismatched types that will be picked up as compilation errors:

The improved type checking for @Select will result in the following:

The following code demonstrates mismatched types that will be picked up as compilation errors:

We also get improved type inference for store.select, store.selectOnce and store.selectSnapshot:

Ref: Proposal, PR #1436

💥 New Lifecycle Hook: ngxsOnChanges

We have added a new lifecycle hook. It was inspired by the onChanges hook available in Angular. It was a very simple change that enabled us to add this hook and it opens the opportunity for some great use cases. The new hook looks like this:

The method receives the NgxsSimpleChanges object that contains the current and previous property values as well as a flag to tell you if this is the first change.

This new hook is very convenient if we want to dispatch any additional actions or respond in any way after any fields within that part of the state have changed.

Lifecycle Sequence

After creating the state by calling its constructor, NGXS calls the lifecycle hook methods in the following sequence at specific moments:

HookPurpose and Timing

  • ngxsOnChanges(): Called before ngxsOnInit() and whenever the state changes.
  • ngxsOnInit(): Called once, after the first ngxsOnChanges() and before the APP_INITIALIZER token is resolved.
  • ngxsAfterBootstrap(): Called once, after the root view and all its children have been rendered.

Let’s look at a couple of simple examples:

I. A convenient way to track state changes:

Before

One of the problems is that if we are not using the @ngxs/logger-plugin or @ngxs/devtools-plugin, then we do not know what the previous state was before our state changed. With this new lifecycle hook we can get quick, simple and focused debugging.

After

II. Convenient to synchronize with the server

Sometimes we need to save the state to the server whenever it is changed in the client.

Before

After

Ref: Proposal, PR #1389

🔧 Other Fixes

  • Add explicit typings for state operators to fix issues with strict mode in typescript Issue, PR #1395, PR #1405
  • Warn if the zone is not actual “NgZone” PR #1270
  • Do not re-throw error to the global handler if custom handler is provided. Issues: #1145, #803, #463, PR: #1379

🔌 Plugin Improvements and Fixes

Router Plugin

  • Fix: Router Plugin — Resolve infinite redirects and browser hanging #1430

In the 3.5.1 release we provided the fix for a very old issue, where the Router Plugin didn't restore its state after the RouterCancel action was emitted. This fix unfortunately introduced a new bug that caused endless redirects and, as a result, crashed the browser! The above PR resolves both issues. 🎉

HMR Plugin

  • Feature: HMR Plugin — Add hmrIsReloaded utility #1435

If you make any changes in your application during the ngOnDestroy Angular lifecycle hook on any of your primary application components then those changes could possibly have side effects for Hot Module Replacement.

Up until now there hasn’t been any way to work around this… Now you can easily control this situation.

hmrIsReloaded - returns true if the application was hot module replaced at least once or more.

Storage Plugin

  • Feature: Storage Plugin — Use state classes as keys #1380

Currently, if you want a specific part of the state of the application to be stored then you have to provide the path of the state to the storage plugin. Now we have added the ability to provide the state class or a state token instead of the path. As an example, given the following state:

We would previously have to use the path of the state:

Now you can use the state class:

This is a great improvement because it removes the requirement for you to provide the path of a state in multiple places in your application (with the risk of getting out of sync if there are any changes to the path). Now you can just pass the reference to the state class (or the state token) and the plugin will automatically translate it to the path.

Form Plugin

The form plugin UpdateFormValue action has been enhanced with the ability to specify a propertyPath parameter in order to update nested form properties directly.

Before

new UpdateFormValue({ value, path })

After

new UpdateFormValue({ value, path, propertyPath? })

Given the following state:

The state contains information about the new novel name and its authors. Let’s create a component that will render the reactive form with the ngxsFormdirective to connect the form to the store:

Now, if we want to update the name of the first author in our form from anywhere in our application we can leverage the new property. The code will look as follows:

Ref: Issue #910, Issue #260, PR #1215

WebSocket Plugin

There is a new action for the @ngxs/websocket-plugin.

WebSocketConnected - This action is dispatched when a web socket is connected. Previously we only had actions for WebSocketDisconnected and WebSocketConnectionUpdated. This new action completes the picture to allow for observing the web socket connection lifecycle.

Ref: PR #1371

🔬 NGXS Labs Projects Updates

New Labs Project: @ngxs-labs/data

Announcing @ngxs-labs/data

The Problem:

As an application grows larger the number Action classes increases. Often these actions are merely a call through to the state. In this case the abstraction that actions provide may be of less value than the extra effort required to maintain them. Let’s look at the following state:

As you can see, for each method we need to write an action and our component would look like this:

If you would rather not write actions in your application then the @ngxs-labs/data plugin is for you!

How This Plugin Helps:

By leveraging this plugin you can remove the need to create actions for the operations in your state. Your state now acts as a facade where its public methods can be invoked directly and the state operation will be invoked accordingly (behind the scenes a runtime action is created and dispatched, and the declared function handles the action).

And the component now looks like this:

As you can see you can now call methods of your state directly and you no longer need to worry and come up with actions to communicate with your state.

As with anything, there are trade-offs to this approach, so please ensure that you have read about the benefits to CQRS before abandoning the idea of Actions. On the other hand, this is a much simpler approach to state management that may be a better fit for your team and your product.

New Labs Project: @ngxs-labs/actions-executing

Announcing @ngxs-labs/actions-executing

The Problem:

Sometimes we want to wait for some action to complete and we want to update our UI accordingly to represent this pending state. The most common pattern for doing this is as follows:

What started as a UI concern has now littered our state with extra code. We will also end up having selectors to expose this property.

Is there another approach we can take to address this need more cleanly?

How This Plugin Helps:

We can now use the @ngxs-labs/actions-executing plugin to easily determine if an action is being executed and control UI elements or application flow in response. In order to use this plugin we need to load the plugin in our AppModule as follows:

The state becomes much simpler with the pending property removed:

By leveraging the plugin our component can display a loading spinner or disable a button while an action is executing. The component code will now become:

The actionsExecuting selector can be used to track one, many or even all actions (by leaving out the parameter). Included in the data it returns is the count of the executing actions by action type.

There is a great article from the author of this plugin here:

Some Useful Links

If you would like any further information on changes in this release please feel free to have a look at our changelog. The code for NGXS is all available at https://github.com/ngxs/store and our docs are available at https://ngxs.io/. We have a thriving community on our slack channel so come and join us to keep abreast of 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