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
)

Last updated