Why ReactJS Is Right for Salesforce Commerce Cloud?

/, latest blog/Why ReactJS Is Right for Salesforce Commerce Cloud?

ReactJS is an open-source JavaScript library widely used for building composable user interfaces. Designed by Facebook, it helps create rich web apps with minimal coding. It’s commonly used for single page application and mobile applications. For instance, it supports handling view layer for web and mobile apps. A web app developer can easily break down complex UI into simpler components using ReactJS.

Why ReactJS?

  • ReactJS builds web app UIs using reusable UI components instead of templates or HTML directives.
  • ReactJS is fast, scalable and developer-friendly.
  • ReactJS has extensive debugging and design tools for developers.

How does ReactJS integration with Salesforce Commerce Cloud work?

To begin with, working with the DOM API could be truly challenging for developers. ReactJS uses the virtual DOM functionality which helps to construct the HTML DOM more efficiently. This is exactly why it’s the most preferred JS library for front-end development. However, it’s evident that all e-commerce sites are multi-page applications. As a result, we need to invoke the ReactJS elements in every page in order to render the HTML content found in each and every e-commerce webpage.  

  • Babel is a JavaScript compiler which can convert the JSX script to plain JavaScript. Initially, the ReactJS is compiled using Babel and a fully compiled JavaScript file is created. The compiled JS file is then included in the header of the e-commerce site. Ultimately, the JavaScript file will get executed on every page of the e-commerce site.
  • The system should have the Node.js setup in order to install the packages including ReactJS, react-dom, babel-core, babel-loader etc.
  • Salesforce Commerce Cloud code base already has the Node.js set up in the system. It uses the node to compile the CSS and JavaScript for the e-commerce site. In this instance, integrating ReactJS with Salesforce Commerce Cloud the node setup has to include the ReactJS library as well. The developer needs to open command prompt and navigate to the project folder. The required react packages can be installed in the project folder using the command.

Install the packages – ReactJS, react-dom, babel-core, babel-loader to Salesforce Commerce Cloud. Here’s how it’s done!

npm install react --save

npm install react-dom --save

npm install babel-core --save-dev

npm install babel-loader --save-dev

On the page where we have to load the content we place a div structure which will trigger the controller ReactComponent.js.

<div class="render" data-json="<isinclude url=${URLUtils.url('ReactComponent-DynamicShow', 'type', 'fieloProductDetails')}/>"></div>

In the URL, we are passing the value ‘type’ as ‘fieloProductDetails’.  The controller has a switch function which executes based on this value. In this switch case we will fetch the backend data in order to construct a JSON with required values. This JSON is placed on the HTML DOM. The react component will read these JSON and trigger the react component to populate the content. In the JSON we place a field called ‘componentName’, this represents the react component to be executed.

Sample code of ReactComponent.js

'use strict';

/**

* Controller that renders the react json components.

*

* @module controllers/ReactComponent

*/

/* API includes */

var Site = require('dw/system/Site');

var ProductMgr = require('dw/catalog/ProductMgr');

/* Script Modules */

var server = require('server');

server.get('DynamicShow', function (req, res, next) {

var jsonData = {};

if (request.httpParameterMap.type.value != null) {

switch (request.httpParameterMap.type.value) {

case 'fieloProductDetails':

var currentProduct = ProductMgr.getProduct('25502346');

jsonData = {

componentName: 'FieloProductDisplay',

productDescription: currentProduct.longDescription.source,

productName: currentProduct.name,

productImage: currentProduct.getImage('large', 0).getAbsURL().toString()

};

break;

default:

jsonData.error = 'Unknown request type';

}

}

var jsonString = JSON.stringify(jsonData);

res.render('react/staticshow', {jsonData: jsonString});

next();

});

module.exports = server.exports();

The react modules created will have a .jsx extension. Each react module will be created for each rendering of the content. The index.jsx file created will read the DOM elements on a page and in turn will trigger the react component. This step allows us to pass the JSON value to the react component which is then used to create the content.

Sample code for index.jsx

import React from 'react';

import PropTypes from 'prop-types';

import { render } from 'react-dom';

const allComponents = require.context('./containers/', false, /\.js$/);

const resolveDependencies = components => {

return Promise.all(

components.map(

component =>

new Promise(resolve => {

try {

resolve({

func: allComponents(`./${component.name}.js`).default,

...component

});

} catch (e) {

// eslint-disable-next-line no-console

console.warn('Cannot resolve module: ', component.name, '\n', e);

resolve({ error: e, ...component });

}

})

)

);

};

const initializeComponents = () => {

//find all react components on the page

let toRender = document.querySelectorAll('.render:not(.jsx-processed)');

//parse HTMLCollection extracting data JSON and preserving reference to DOM element for further rendering

let components = Array.prototype.reduce.call(

toRender,

(result, element) => {

try {

let data = JSON.parse(element.getAttribute('data-json'));

result.push({ name: data.componentName, data: data, target: element });

element.classList.add('jsx-processed', 'jsx-' + data.componentName);

return result;

} catch (e) {

// eslint-disable-next-line no-console

console.warn('Error parsing component data - skipping: ', element);

return result;

}

},

[]

);

//resolve module dependency for all found components

resolveDependencies(components)

.then(components => {

components.map((component, i) => {

//handle the case when some of the components might have not been found

if (!component.error) {

//by convention component name has to be capitalized, otherwise JSX treats it as html tag

let Comp = component.func;

//wrap each component in Provider to give it access to store and render into target DOM

render(

<Comp {...component.data} />,

component.target

);

} else {

if (!config.isProd) {

const Error = props => {

let style = {

background: 'rgba(255,200,200,0.9)',

fontWeight: 'bold',

color: '#FF0000'

};

return (

<div className="error-component" style={style}>

<strong>{props.name}: </strong>

{props.error.message}

</div>

);

};

Error.propTypes = {

name: PropTypes.string,

error: PropTypes.string

};

render(<Error {...component} />, component.target);

}

}

});

})

.catch(e => console.error(e)); // eslint-disable-line no-console

};

const observer = new MutationObserver(mutationsList => {

for (var mutation of mutationsList) {

if (mutation.type == 'childList' && mutation.addedNodes.length > 0) {

initializeComponents();

}

}

});

document.addEventListener('DOMContentLoaded', initializeComponents);

In the react component we can get the JSON values with the keyword ‘this.props’. This is how we pass the backend values to the react component to create the html content.

Sample code

import React from 'react';

import ReactDOM from 'react-dom';

class FieloProductDisplay extends React.Component {

   render() {

       return (

       <>

        <p>Hello world React</p>

        <div>{this.props.productName}</div>

        <div>{this.props.productDescription}</div>

        <img src={this.props.productImage} />

       </>

       );

   }

}

export default FieloProductDisplay;

With ReactJS, the HTML element is created only on the time of rendering the page in the e-commerce site.  It considerably reduces the size of DOM which results in lower page load time and higher site performance. Why invest a lot of time and effort in using a framework when you get better functionalities and greater efficiency with ReactJS? The simplicity, native approach, performance and testability of ReactJS are key factors in making it the right choice for integration with Salesforce Commerce Cloud.


Zora