Gentics Mesh React - Event Handling

Intro

In this guide which is a follow-up guide to the React Basics Guide you will learn how events can be used to invoke live-reload of contents.

Eventbus

Gentics Mesh features an eventbus system which is used to handle all internal and public events. Events will for example be dispatched once a content gets updated, deleted or created. This eventbus is can be accessed in a SockJS compliant way which also includes websockets. Once connected to the websocket you can subscribe to specific types of events. You can read more about the Event system in our Events documentation.

We can use the eventbus bridge in our app to reload the components once new content has been added mesh.node.created, removed mesh.node.deleted and updated mesh.node.updated.

React App Vehicle Page

App

The following changes can be applied on the guide-step-2 branch.

The fully working example can be found in the guide-step-3 branch of the mesh-react-example repository.

The proxy field in the package.json file specifies the Gentics Mesh server to be proxied into the app server. By default https://demo.getmesh.io will be used.

First we need to add the vertx3-eventbus-client which provides the connection to the websocket endpoint of Gentics Mesh.

npm install vertx3-eventbus-client

Next we create the eventbus.js file which contains the needed code to register callbacks and event subscription.

eventbus.js
import EventBus from 'vertx3-eventbus-client';
import { useEffect } from 'react';

let eb = new EventBus(":apiLatest:/eventbus")
eb.enableReconnect(true);

let callbacks = [];

eb.onopen = function () {
    console.log("Connect eventbus");
    registerEvent("mesh.node.updated", callbacks);
    registerEvent("mesh.node.created", callbacks);
    registerEvent("mesh.node.deleted", callbacks);
}

function registerEvent(eventName, callbacks) {
    eb.registerHandler(eventName, function (error, message) {
        callbacks.forEach(cb => cb());
        console.log('Received a message: ' + JSON.stringify(message));
    });
}

export default function useWebsocketBridge(callback) {
    useEffect(() => {
        callbacks.push(callback);
        console.log("Mount");
        return () => {
            var index = callbacks.indexOf(callback);
            if (index > -1) {
                callbacks.splice(index, 1);
            }
            console.log("Unmount");
        }
    });
}

Lets now declare a GraphQL query method which will load the content for the given path and return a promise with the result data.

api.js
…

export async function getNodeByPath(path) {
  return graphQl(`
  query Webroot($path: String) {
    node(path: $path) {
      schema {
        name
      }
      ...category
      ...product
    }
  }
  ${categoryFragment}
  ${productFragment}
  `, {path}).then(response => response.node);
}

…

Finally we need to alter the Content.jsx file.

First we need to declare a State Hook for our component which keeps track of the content that is used in the component.

Next we register the callback for the websocket event handler using the useWebsocketBridge method. This method will update the state using the update function setNodeResponse of the state once one of the three events (update, delete, create) has been received.

Finally we want to use the Effect Hook to trigger the update of the state once the location.pathname variable changes. Without this effect the component would not be rendered again once a different webroot content gets loaded.

Content.jsx
import React, { useState, useEffect } from 'react';
import { getNodeByPath } from './api';
import useWebsocketBridge from './eventbus';

…

const WebrootContent = ({ location }) => {
  // Create state for the component
  const [nodeResponse, setNodeResponse] = useState();

  // Register event callback to update the state when content gets changed in Gentics Mesh
  useWebsocketBridge(() => {
    getNodeByPath(location.pathname).then(setNodeResponse);
  });

  // Use effect hook to set the content when the path changes
  useEffect(() => {
    getNodeByPath(location.pathname).then(setNodeResponse);
  }, [location.pathname]);

  if (!nodeResponse) {
    return null;
  }

  const NodeComponent = NodeComponents[nodeResponse.schema.name];
  return <NodeComponent node={nodeResponse} />
}

…

Testing

Once you applied the changes you should be able to open the react app and in parallel use the Gentics Mesh UI to update the contents. The changes should directly be visible in the app.

Final thoughts

The shown event mechanism will update any NodeContent component that is in-use. It would however also possible to only reload components which render a specific element. The event that contains the information that the Space Shuttle Vehicle was updated would thus only update the component which renders the Space Shuttle.