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
Here we will see the OOPS (Object Oriented Programming Structure) way of introducing Platform Events in ReactJS.
The concept being defined here is
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
listOfComponentsToUpdate
variable in cometdConnection.js
Tadaaaa… Done. No extra steps in future. Just life made simple. Isn’t it!?!
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
☞ The Complete Web Development Tutorial Using React and Redux
☞ 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