NGXS with HMR Plugin

Ivanov Maxim
NGXS
Published in
5 min readFeb 28, 2019

Introduction

Hot Module Replacement (HMR) is a Webpack feature to update code in a running app without rebuilding it. This results in faster updates and less full page-reloads. You can read more about HMR by visiting this page.

How does it work?

HMR is a way of exchanging modules in a running application (and adding/removing modules). You basically can update changed modules without a full page reload.

From the app view

The app code asks the HMR runtime to check for updates. The HMR runtime downloads the updates (async) and tells the app code that an update is available. The app code asks the HMR runtime to apply updates. The HMR runtime applies the updates (sync). The app code may or may not require user interaction in this process (you decide).

From the webpack compiler view

In addition to the normal assets, the compiler needs to emit the “Update” to allow updating from a previous version to this version. The “Update” contains two parts:

  1. Update manifest (json)
  2. One or multiple update chunks (js)

The manifest contains the new compilation hash and a list of all update chunks. The update chunks contain code for all updated modules in this chunk (or a flag if a module was removed). The compiler additionally makes sure that module and chunk ids are consistent between these builds. It uses a “records” json file to store them between builds (or it stores them in memory).

From the module view

HMR is option feature, so it only affects modules that contains HMR code. The documentation describes the API that is available in modules. In general, the module developer writes handlers that are called when a dependency of this module is updated. They can also write a handler that is called when this module is updated. In most cases, it’s not mandatory to write HMR code in every module. If a module has no HMR handlers, the update bubbles up. This means a single handler can handle updates for a complete module tree. If a single module in this tree is updated, the complete module tree is reloaded (only reloaded, not transferred).

From the HMR runtime view

Additional code is emitted for the module system runtime to track module parents and children. On the management side, the runtime supports two methods: check and apply.

A check does a HTTP request to the update manifest. When this request fails, there is no update available. Elsewise the list of updated chunks is compared to the list of currently-loaded chunks. For each loaded chunk, the corresponding update chunk is downloaded. All module updates are stored in the runtime as updates. The runtime switches into the ready state, meaning an update has been downloaded and is ready to be applied.

For each new chunk request in the ready state, the update chunk is also downloaded.

The apply method flags all updated modules as invalid. For each invalid module, there needs to be a update handler in the module or update handlers in every parent. Else the invalid bubbles up and marks all parents as invalid too. This process continues until no more "bubble up" occurs. If it bubbles up to an entry point, the process fails.

Now all invalid modules are disposed (dispose handler) and unloaded. Then the current hash is updated and all “accept” handlers are called. The runtime switches back to the idle state and everything continues as normal.

Technical view

Enabling HMR in Angular

In order to get HMR working with Angular CLI we first need to add a new environment and enable it. Next we need to update the bootstrap process of our app to enable the @angularclass/hmr module.

Add environment for HMR

Create a file called src/environments/environment.hmr.ts with the following contents:

Update src/environments/environment.prod.ts and add the hmr: false flag to the environment:

Update src/environments/environment.ts and add the hmr: false flag to the environment:

Update angular.json to include an hmr environment as explained here and add configurations within build and serve to enable hmr. Note that <project-name> here represents the name of the project you are adding this configuration to in angular.json.

Add the necessary types to src/tsconfig.app.json

Run ng serve with the flag --configuration hmr to enable hmr and select the new environment: $ ng serve --configuration hmr

Create a shortcut for this by updating package.json and adding an entry to the script object:

"scripts": {
...
"hmr": "ng serve --configuration hmr"
}

Add dependency for @angularclass/hmr and configure app

In order to get HMR working we need to install the dependency and configure our app to use it. Install the @angularclass/hmr module as a dev-dependency: $ npm install @angularclass/hmr --save-dev

Create a file called src/hmr.ts with the following content:

Update src/main.ts to use the file we just created:

Starting the development environment with HMR enabled

Now that everything is set up we can run the new configuration:

$ npm run hmr
Simple example of working HMR

Problem

Actually, there is no real hot reload in Angular. This appeared to be promising, but after trying a simple test, this isn’t actually doing true hot module reloading.

Does not keep local state

By default HMR automatically loses state, no matter which framework you use, unless you’re using something like Redux state. So yeah, it’s default behavior way no worries!

Preserve state during hot reload

To make HMR work as need the way our team developed a plugin. At today’s number, the plugin is in beta version, so you can install it in this way:

$ npm install @ngxs/hmr-plugin --save-dev

Official release is planned in version 3.4.0.

What’s next

You just need to do the following: remove the hmr.ts file, and update the main.ts:

Enjoy your HMR with NGXS

HMR with keep states

How does it work?

Plugin supported only with angular 6+

NGXS HMR Lifecycle

If you want to do some modifications to the state during the hmr lifecycle you can use these built-in actions. They will not be executed in production.

--

--

Ivanov Maxim
NGXS
Writer for

Code 🤖, Bike 🚵 and Music 🎶 Teams: @splincodewd ★ @Angular-RU ★ @ngxs ★ github.com/splincode