ng-state
  • Introduction
  • Main differences
  • Getting Started
    • Instalation
    • Examples
  • Core concepts
    • Main idea
      • More complex flow visualization
    • Configuration
      • Immer Setup
      • ImmutableJs Setup
    • Store
      • Operators
      • Optimistic updates plugin
      • Optimistic updates plugin
      • Form manager plugin
        • onChange hook
        • shouldUpdateState hook
        • Custom form elements
      • Persist state plugin
        • Configuring custom storage
    • Actions
      • Immer
      • ImmutaleJs
      • Injectable Actions
    • Components with Actions
      • Signal Actions
      • State Actions
    • @InjectStore decorator
    • @WithStore decorator
    • @ComponentState decorator
    • Dispatcher
  • Different scenarios
    • Passing list item index via router
    • ngOnChanges hook
    • FormManager pitfalls
  • Unit testing
    • Setup
    • Test store
    • Test actions
    • Test component with actions
    • Test with Angular TestBed
  • Debugging
    • Setup
    • Redux DevTools
    • Automated changes output
    • Manual state changes check
    • Additional debugging information
  • Production
    • Production mode
    • Server Side Rendering (SSR)
  • Other information
    • Best practices
    • CLI
      • Custom Configurations
    • Performance
    • Blog Posts
    • Contributing
Powered by GitBook
On this page

Was this helpful?

  1. Production

Server Side Rendering (SSR)

When using Server Side Rendering you would want to avoid double data load for same page. To atchieve this you have to do two steps:

  1. Save state when on server

  2. Load state when on browser

this can be attchieved in your app.module.ts like:

export class AppModule {
    private transferStateKey = makeStateKey(TRANSFER_STATE_KEY);

    constructor(
        private store: Store<MarketplaceState>,
        private platform: PlatformService,
        private transferState: TransferState,
        private dataStrategy: DataStrategy,
    ) {
        if (!this.platform.isBrowser) {
            this.saveState();
        } else {
            this.loadState();
        }
    }

    private saveState() {
        this.store.pipe(takeWhile(() => !this.platform.isBrowser)).subscribe((state) => {
            this.transferState.set(this.transferStateKey, this.dataStrategy.toJS(state));
        });
    }

    private loadState() {
        if (!this.transferState.hasKey(this.transferStateKey)) {
            return;
        }

        const state = this.transferState.get(this.transferStateKey, {} as any);
        this.store.update((s) => {
            this.dataStrategy.merge(s, state, [], true);
            s.dataRestoredFromSSR = true;
        });

        this.removeRestoredFromSSRFlag();
    }

    private removeRestoredFromSSRFlag() {
        document.addEventListener('readystatechange', (event: any) => {
            if (event.target.readyState === 'complete') {
                this.store.update((s) => {
                    s.dataRestoredFromSSR = false;
                });

                document.removeEventListener('readystatechange', (_) => {});
            }
        });
    }
}

Lets break this code

  1. saveState method saves all state changes until on server

  2. loadState method checks if saved state exists and if so loads it and merges onto the store and sets dataRestoredFromSSR to true. After we listen to page finish loading event in order to set dataRestoredFromSSR back to false.

dataRestoredFromSSR allows us to prevent all http calls until page is loaded - meaning that we will not hit server with repetetive requests for data that has been already loaded and restored.

Our http requests might look like this

this.http.post(url, body).pipe(
    withLatestFrom(this.store.map((s) => s.dataRestoredFromSSR)),
    filter(([_, dataRestoredFromSSR]) => !dataRestoredFromSSR),
    map(([response, _]) => response),
    //do the rest like .subscribe
)
PreviousProduction modeNextBest practices

Last updated 2 years ago

Was this helpful?