Angular 2 : Thoughs about HMR and @ngrx/store
Asked Answered
L

2

3

Before starting, this link can be useful : what is the purpose of HMR ?.

I do not have a deep experience in huge projects management / conception. I'm still to young to have a beautiful beard. So I am here, looking for advices.

the seed

I am looking and thinking at this seed of angular 2 and I am wondering if using HMR is a viable option to develop a large application and how to do that.

Those are just thoughs, I just want to discuss with you to make my decision. We all need to cross our experiences :-)

data structure

Based on this example :

@Component({
  selector: 'home',
  templateUrl: `
    <form (ngSubmit)="submitState(localState.value)" autocomplete="off">

      <input
        [value]="localState.value"
        (input)="localState.value = $event.target.value"
        placeholder="Submit Local State to App State">

      <button>Submit Value</button>
    </form>
  `
})
export class HomeComponent {
  localState = { value: '' };

  constructor(public appState: AppState, public title: Title) {}

  onSubmit(value: string) {
    this.appState.set('value', value);
    this.localState.value = '';
  }
}

By using the appState, components can be dynamically reloaded and data can be injected. That sounds cool. But here is my first question : to enable this feature, components have to have a place in the appstate tree, so it's a good idea to use a localState like in the example. This force us to play with an object inside our components, which can be huge. It can be annoying. Do you agree with that ? Can it be an issue on a large application ? (I'm going to work on a large app).

It can be nothing because I only have to store in my localStorage data which I want to be tracked by HMR. So why not.

data storage

Speaking about storage, I am also using @ngrx/store as an implementation of Redux. It seems to be a really good idea because the state can be my localStorage. When the app is updated, I just need to rehydrate my state and it works. Data in component will not be tracked by hmr, only the state will be.

Sounds great. But is it a good idea ? I've tried to find a connector to take care about communications between @ngrx/store and hmr. Victory ! but this package seems to be outdated.

I can do this by myself but is it a good idea ? Angular's services will use reducers and I can find hooks to update hmr's state.

I think that @ngrx/store and hmr are well kwnon by angular developpers. Those 2 technologies seems to fit well but I don't find a lot of ressources over here. So I'm thinking there must be an issue I don't see right now.

I wrote all of this to ask you advices about your experience with those technologies / problematics.

Do you see cons to combine those technologies ? I'm experimenting but I can't think about all the cases. That is why I need your help.

summary

  • Is HMR ready for "production" ?
  • Is it okay to move a lot of information inside @ngrx/store (things like 'this checkbox is checked', 'the current value of this input is foobar'...)
  • Most important : Do these technologies fit well ?

Those questions remind me https://github.com/CodeSequence/ngrx-store-hmr/issues/2.

Lannylanolin answered 15/11, 2016 at 14:52 Comment(1)
The stack you are using is too new, that is why there are so few posts on them and many are outdated being only a few months old. Hope you get some good answers cheers.Garaway
H
8

TL;DR - HMR is (in most cases) a tool you use in development. Develop your apps like you would without it, don't think too much about how it works. If things break, just reload the page. On the long run it will still save you a lot of time...

Angular apps are big (few megabytes) and it takes time to compile them. HMR is used in development to speed up things so you don't have to wait few seconds to see the result each time you make a change in your code. HMR works by utilizing a loader you are already using (most commonly webpack and/or systemjs). OK, you probably know all this (;

I recently implemented a custom HMR based on Systemjs (using systemjs-hmr). Basic principal is: when you change your code, notify the loader and reload changes. The rest is just cleanup to make sure your app is still working...

My HMR is pretty basic:

  • reload changed app code (components, services, pipes....),
  • recreate <app-root>, like this,
  • destroy old app with NgModuleRef.destory(),
  • bootstrap new app with: platformBrowserDynamic().bootstrapModule(AppModule).then(module => window['__HMR__'] = module)
  • get @ngrx/store initial value

This is the code I used:

import { Store } from '@ngrx/store';
export function get__HMR__state() {
  let state = {};
  if (window['__HMR__']) {
    const module = window['__HMR__'];
    const store = module.injector.get(Store);
    store.take(1).subscribe(s => state = s);
  }
  return state;
}

@NgModule({
  imports: [
    StoreModule.provideStore(AppReducer, get__HMR__state())
  ]
})

I don't store anything I don't need in my app in the AppState (ngrx/store). Inputs values and attributes are better handled like this.

It works pretty well, I usually start dev process (using custom gulp build workflow) and forget about it for the rest of the day (; Thing do break, specially when I mess with project structure and dependencies, but quick F5 and everything is working again. When I work with templates and styles (which is the most painful part if you have to wait long for changes) reloads work perfectly.

I'm using angular-cli for "project management" and when I develop with it's ng serve (which uses webpack) I get rebuild time of 3-8 seconds + 2-4 seconds to reload the page. That's for any change I make in code. With my Gulp+HMR (on the same cli project) I get rebuild time of 50-400ms + 200-500ms reload. Which is significant and the reason I made a custom build (; If things break, hit F5 and after 2-4 seconds app is working again.

To close, I highly recommend HMR for anyone who spends few hours/day coding (; It is not perfect, but no tool is, and this one saves a lot of time on the long run. However, don't change your app to fit the tool. Write code as you would without HMR. If you need something extra and tool can't do that, you can always extend it - it's just a javascript (;

Heartfelt answered 16/11, 2016 at 7:55 Comment(0)
L
2

I think this repo is very instructive.

This seed is using Angular 2, HMR, @ngrx/store and so on. I learn a lot by reading the code. @ngrx/effects seems a good way to go and the use of both @ngrx/store and hmr is easy, just look at the app.moudle.ts. Thanks to AngularClass/hmr-loader, all is here :

hmrOnInit(store) {
    if (!store || !store.rootState) return;

    // restore state by dispatch a SET_ROOT_STATE action
    if (store.rootState) {
      this._store.dispatch({
        type: 'SET_ROOT_STATE',
        payload: store.rootState
      });
    }

    if ('restoreInputValues' in store) { store.restoreInputValues(); }
    this.appRef.tick();
    Object.keys(store).forEach(prop => delete store[prop]);
  }
  hmrOnDestroy(store) {
    const cmpLocation = this.appRef.components.map(cmp => cmp.location.nativeElement);
    this._store.take(1).subscribe(s => store.rootState = s);
    store.disposeOldHosts = createNewHosts(cmpLocation);
    store.restoreInputValues = createInputTransfer();
    removeNgStyles();
  }
  hmrAfterDestroy(store) {
    store.disposeOldHosts();
    delete store.disposeOldHosts;
  }
Lannylanolin answered 17/11, 2016 at 14:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.