Sitecore JSS is really powerfull framework which brings a lot of advantages to your websites. After a few days of digging into this technology, I fell in love with this headless approach which is great and I can see a bright future ahead of this. Once I’ve started playing with it, I noticed that only JSON data is coming from a server and we’re building whole HTML on client side. Based on that, I decided to put some challenges for myself to find a way how we can use it for a real time personalization - it means that content will be changed automatically once provided rule will be fulfilled and all will be done without any page reloading. Long story short - it is enough to get newest data from API and replace it in the store. Let’s take a look at what I found out.

Let’s start

In this article I’ll use JSS with Vue JS in connected mode and I’ll base my mechanism on RouteHandler.vue from JSS offitial github. As I am mainly back end developer, please forgive me any potenial lacks in JS code, and if you find something to improve, please let me know.

Monitoring changes

To get information when personalized content is changed, we have to keep updated information and refresh it from time to time. For this purpose we need to create worker which will make calls to get up to date route data using sitecore API. Also, to avoid unnecessary refereshing components that contain any changes, it would be good to check if old and new object values are deeply equal (Just to clarify, == and === will not fly here, because they compare instances of the objects). For this purpose I’ll use lodash.isequal.

...
import isEqual from 'lodash.isequal';
import EventBus from '../../Foundation/Events/EventBus';

export default {
  name: 'Route-Handler',
  data() {
    return {
        ...,
        interval: null,
        };
  },
  created(param) {
    ...
    this.enableWorker();
  },
  beforeDestroy() {
    ...
    this.disableWorker();
  },
  methods: {
    ...,
    updateComponents() {
      getRouteData(this.sitecoreRoutePath, this.language).then((routeData) => {
        if (routeData !== null && routeData.sitecore.route) {
            // here we're checking if route data returned from api is different than current one
          if (!isEqual(this.appState.routeData, routeData.sitecore.route)) {
            //in case if it is, we have to change data in actual jss store
            this.$jss.store.setSitecoreData(routeData);
          }
        }
      });
    },
    enableWorker() {
      if (!this.context.pageEditing && !this.interval) {
        this.interval = setInterval(() => this.updateComponents(), 5000);
      }
    },
    disableWorker() {
      if (!this.context.pageEditing && this.interval) {
        clearInterval(this.interval);
        this.interval = null;
      }
    },
  },
};

Refreshing components

Once we’ve updated route data object in JSS store, we can use Vue watchers to handle moment when we should make our component rendered again. Let’s imagine that I would like to rerender my section component with all children in case if any data within section or children is changed. For this purpose we need to watch rendering property and make a deep comparision between old one and the new one. In case if changes are detected, we’ll trigger code responsible for rerender Section component.

<template>
  <section :class="backgroundClass" :id="sectionId" v-if="renderComponent">
    <div class="container">
      <placeholder name="jss-section" :rendering="rendering" />
    </div>
  </section>
</template>
import { Placeholder } from "@sitecore-jss/sitecore-jss-vue";
import isEqual from "lodash.isequal";

export default {
  data() {
    return {
      renderComponent: true
    };
  },
  props: {
    rendering: {
      type: Object
    }
  },
  components: {
    Placeholder
  },
  watch: {
    rendering(val, oldVal) {
      // here we're checking if route data returned from api is different than current one
      if (!isEqual(val, oldVal)) {
        this.refresh();
      }
    }
  },
  methods: {
    // renderers component
    refresh() {
      this.renderComponent = false;
      this.$nextTick(() => {
        this.renderComponent = true;
      });
    }
  }
};

The nice thing about this solution is that we don’t render whole layout again, but only those places what are handled by our watchers and only those that were changed. Besides real time personalization, usage of this solution can be helpfull in many other situations like:

  • dynamically changed content/datasource without reloading page
  • handling content for singed in/signed off users
  • synchronizing page content opened in few tabs/windows

That’s how it works in practice

On our demo page, I’ve created Section component where I’ve put minor components. For one of them I’ve set personalization rule to use different datasource once specific timestamp will be reached. You will notice that component will be changed automatically without reloading the page. It is good to know that only components from one Section were rerendered, because only there data were changed.

Example

Few words on the end

This soultion can be really helpfull, but we should be aware that traffic on the server will increase, because every active instance will made some requests in background to get up to date data. Once we want to use it on production site, we must observe statistics and choose right interval time for our worker or get rid of worker and make hooks for some events, but it is up to the situation.

Thank you for reading and I hope you enjoyed it.

Updated:

Leave a comment