Platform Events with Salesforce and React

Platform Events with Salesforce and React
Platform Events is a buzz word now in Salesforce being a replacement for Streaming API and yes with Salesforce we can provide a feature to update the UI dynamically like how we see in espn cricinfo, where the scores are updated automatically without any refresh

Platform Events is a buzz word now in Salesforce being a replacement for Streaming API and yes with Salesforce we can provide a feature to update the UI dynamically like how we see in espn cricinfo, where the scores are updated automatically without any refresh. Thanks to the platform events feature.

But how could we use Platform-Events with ReactJS to enable this feature in our react-app when working with Salesforce. Is it easy to do? Are there any direct ways to do so? Hmm I would say the answer is YES.

We will see the cometd way of implementing Platform-Events in this blog

Here we will explore how we can make our life simple with ReactJS to use Platform Events.

For those who don’t know what Platform Events are,
There are so many ways on how we can use Platform-Events with ReactJS. We will see the Object Oriented Way and React.Context way to explain in detail on how we can use Platform-Events

Prerequisites:

npm install cometd --s

Object Oriented Way

Here we will see the OOPS (Object Oriented Programming Structure) way of introducing Platform Events in ReactJS.

The concept being defined here is

  • Configure comted connection when mounting our main App.js → creating a cometd connection with Salesforce, initiate a handshake, have a call back function to do the desired function
  • While configuring the callback as said above, create a custom event with the required data and the corresponding component(s) whichever need to be updated dynamically and trigger the event with reference from App.js
  • Create a intermediate class between your component which needs to be updated dynamically based on action in Salesforce and React.Component, which will catch the event triggered above which has the data as well the name of your component which should be updated. On catch of the custom event, call a callback function which we will see in next point
  • Extend your component to the class created in the previous step and not to React.Component and define a name to the component, which will be identified in the intermediate layer on catch of the custom event triggered and implement the callback function which updates the data dynamically on catch of the event triggered

Does it seems to be complex 😳? Nothing to worry 😃. We will go through it in detail technically.

Configuring CometD

First we will create JS File called cometdConnection.js which is responsible to do the first step mentioned above

import ReactDOM from 'react-dom';
import cometdLib from "cometd";

/* Parameters Explanation
    refElement - 
        refs used in React to represent an element to which the custom event is binded
    protocol - 
        protocol to connect to Salesforce - most probably https:
    hostname - 
        hostname to connect to Salesforce - Most probably the instanceURL of Salesforce
    sessionId - 
        sessionId to authenticate from Salesforce
*/

export const configureCometD = (refElement, protocol, hostname, sessionId) => {
  let cometd = new cometdLib.CometD();

  /* List of components to update once platform event is triggered */

  let listOfComponentsToUpdate = ['myComponent1','myComponent2'];
  
  /* Configuration of cometD library */

  cometd.configure({
      url: protocol+'//'+hostname+'/cometd/40.0/',
      requestHeaders: { Authorization: 'OAuth '+sessionId},
      logLevel: 'debug',
      appendMessageTypeToURL : false
  });
  cometd.websocketEnabled = false;

  /* Try for a handshake to connect to Salesforce through cometD library */

  cometd.handshake(function(handshakeReply) {
      if (handshakeReply.successful) { /* Successful handshake */

      /* Connection Successful - Subscribe to platform event */

      cometd.subscribe('/event/MyPlatformEvent_Object__e',
        function(platformEvent) {
          /* Platform event received */
          if(platformEvent && platformEvent.data.payload) {

            /* Create a customEvent */

            let customEvent = new CustomEvent("updateComponent",  {
              detail: {
                        componentsToUpdate : listOfComponentsToUpdate,
                        data: {
                          /* data to be used from platform events received, 
                             can be used for manipulating the component or any required funtion */
                        }
                      },
              bubbles: true
            });

            /* Dispatch the CustomEvent to the html element from where it will be triggered */

            ReactDOM.findDOMNode(refElement).dispatchEvent(customEvent);

          }
        }
      );
    }
    else
      console.error('Failed to connected to CometD.');
  });
}

Now in our App.js (main js file to render react applications) we do some modifications to configure cometdConnection to connect to Salesforce.

App.js to call for configuring cometD

First import the function exported from cometdConnection.js in App.js

import { configureCometD } from ‘./cometdConnection’;

In the render function of App.js initialize the ref to access the DOM to attach the custom event triggered

render() {
    // Our custom code if we want any
    return (
        <div ref="app">
           /* All our App.js render function's return code*/
        </div>
    )
}

Now in App.js implement the below block in componentDidMount method (life-cycle of react component)

configureCometD(
    this.refs.app, 
    'https:', //protocol to refer to
    'xxx.salesforce.com', //hostname to refer to
    'some random session id' //sessionId obtained from Salesforce
);

Implement a middle layer between our component and React.Component

PlatFormEventComponent.js

import React from 'react';

class PlatFormEventComponent extends React.Component {
    name = 'PlatFormEventComponent';
    constructor(props){        
        super(props);
        document.addEventListener("updateComponent", this.handleUpdateComponent, false);
    }

    componentWillUnmount() {
        document.removeEventListener("updateComponent", this.handleUpdateComponent, false);
    }

    handleUpdateComponent = (event) => {
        if(event.detail && event.detail.componentsToUpdate && event.detail.componentsToUpdate.indexOf(this.name) >= 0) {
            this.updateComponent(event.detail.data || {});
        }
    }

    updateComponent = (data) => {
        console.log("Im called : " + data);
    }

}

export default PlatFormEventComponent;

Extend our component to respond to platform events triggered

MyComponent1.js

import React from 'react';
import PlatFormEventComponent from "PlatFormEventComponent";
/*
Other required imports for our component
*/

export default class MyComponent1 extends PlatFormEventComponent {
    constructor(props) {
        super(props);
		this.name = 'myComponent1';
    }

    updateComponent(data) {
        /*
        Hurray - We can write the code to update our component
        */
    }

    /*
        Other required functions
    */

    render() {
        return {
            /*
                jsx to render
            */
        }
    }
}

MyComponent2.js

import React from 'react';
import PlatFormEventComponent from "PlatFormEventComponent";
/*
Other required imports for our component
*/

export default class MyComponent2 extends PlatFormEventComponent {
    constructor(props) {
        super(props);
		this.name = 'myComponent2';
    }

    updateComponent(data) {
        /*
        Hurray - We can write the code to update our component
        */
    }

    /*
        Other required functions
    */

    render() {
        return {
            /*
                jsx to render
            */
        }
    }
}

Now just to make fun we will create a third component which doesn’t require any update from platform events created

MyComponent3.js

import React from 'react';
import PlatFormEventComponent from "PlatFormEventComponent";
/*
Other required imports for our component
*/

export default class MyComponent3 extends PlatFormEventComponent  {
    constructor(props) {
        super(props);
	this.name = 'myComponent3'; // Just for fun i have name here :P
    }

    updateComponent(data) { // Just for fun i implemented the method here :P
        /*
        Hurray - We can write the code to update our component
        */
    }

    /*
        Other required functions
    */

    render() {
        return {
            /*
                jsx to render
            */
        }
    }
}

That’s all. Platform events with Reactjs is successfully implemented.

So as we see, any platform event reported from Salesforce, MyComponent1 and MyComponent2 will be updated with the updateComponent call, but MyComponent3 will never be updated even though it is extending the middle layer

Now once this setup is done, just relax and in future if you want a component to be updated by the platform event, just follow the below steps

  1. Add the name in listOfComponentsToUpdate variable in cometdConnection.js
  2. Create your own component extending PlatFormEventComponent and give it a name as added in Step 1 and implement the method updateComponent

Tadaaaa… Done. No extra steps in future. Just life made simple. Isn’t it!?!

React Context Way

This is same as Object Oriented Approach. Only thing is instead of CustomEvent triggering, we can use React.Context to do the magic

Here we can create a React Context globally and update the context each and every time when cometD channel gets the value and also update the context with the name of the components to be re-rendered.

We need to wrap the App component to include the Context Provider created by React Context. In the respective components which needs to be updated on trigger of platform event, we need to use Context Consumer to catch hold of the event and get the required data to update the component.

App.js with ReactContext configured with Platform Event Subscriptions

import React, { Component } from 'react'
import cometdLib from "cometd";

export const PlatformEventContext = React.createContext()

export default class App extends Component {
    constructor(props) {
      super(props);
      this.state = {
        platformEventInformation : {
          componentsToUpdate : [],/* List of components to update - can be used to update the components dynamically */
          data : {},/* Required data to pass to component on successful receive of platform events*/
          /* Other state related informations */
        }
      };
    }
    
    componentDidMount() {
      let cometd = new cometdLib.CometD();

      /* List of components to update once platform event is triggered */

      let listOfComponentsToUpdate = ['myComponent1','myComponent2'];

      /* Configuration of cometD library */
      /* Replace the below variables with correct values for protocol, hostname and sessionId 
         Say protocol to be 'http:'
         Say hostname to be our salesforce instance
         Say sessionid to be the sessionId obtained from Salesforce
      */
      cometd.configure({
          url: protocol+'//'+hostname+'/cometd/40.0/', 
          requestHeaders: { Authorization: 'OAuth '+sessionId},
          logLevel: 'debug',
          appendMessageTypeToURL : false
      });
      cometd.websocketEnabled = false;

      /* Try for a handshake to connect to Salesforce through cometD library */

      cometd.handshake(function(handshakeReply) {
          if (handshakeReply.successful) { /* Successful handshake */

          /* Connection Successful - Subscribe to platform event */

          cometd.subscribe('/event/MyPlatformEvent_Object__e',
            function(platformEvent) {
              /* Platform event received */
              if(platformEvent && platformEvent.data.payload) {

                /* set the state to update the React Context */
                this.setState({
                  platformEventInformation : {
                    componentsToUpdate : listOfComponentsToUpdate,
                    data: {
                      /* data to be used from platform events received, 
                         can be used for manipulating the component or any required funtion */
                    }
                  }
                });
              }
            }
          );
        }
        else
          console.error('Failed to connected to CometD.');
      });
    }

    render() {
        const { platformEventInformation } = this.state;
        return (
            <React.Fragment>
                <PlatformEventContext.Provider value={platformEventInformation}>
                    <Home/>
                </PlatformEventContext.Provider>
            </React.Fragment>
        );

Now lets see rewritten MyComponent1.js ( We are not going to tell about MyComponent2.js here, since it is same as it’s counterpart)

import React from 'react';
import { PlatformEventContext } from "App";
/*
Other required imports for our component
*/

export default class MyComponent1 extends PlatFormEventComponent  {
    constructor(props) {
        super(props);
        this.name = 'myComponent1';
    }

    updateComponent(data) { // Just for fun i implemented the method here :P
        /*
        Hurray - We can write the code to update our component
        */
    }

    /*
        Other required functions
    */

    render() {
        return {
            <React.Fragment>
              <PlatformEventContext.Consumer>
                {( platformEventInformation ) => {
                    if(platformEventInformation && platformEventInformation.componentsToUpdate 
                        && platformEventInformation.componentsToUpdate.indexOf(this.name) >= 0) {
                        this.updateComponent(platformEventInformation.data || {});
                    }
                }}
            </PlatformEventContext.Consumer>
            </React.Fragment>
        }
    }
}

As we saw here, it is exactly the same approach as the first one, but different way to implement it.

Hence proved 😜

Recommended Courses:

React for beginners tutorial

React Native Projects

The Complete Web Development Tutorial Using React and Redux

React From The Ground Up

React for visual learners

Suggest:

Learn React - Full Course for Beginners - React Tutorial 2019

React + TypeScript : Why and How

React Tutorial - Learn React - React Crash Course [2019]

Page Transitions In React - React Router V6 and Framer Motion Tutorial

Getting Closure on React Hooks

Build a React.js application and add Authentication with Auth0