Cubic Compass - Salesforce.com Solutions
  • Home
  • Navigator
  • Services
    • Subscription Services
    • Portfolio
    • CPQ Implementations
  • About
    • Contact

Developing Lightning Applications With ReactJS

10/23/2015

0 Comments

 
Won't you listen 'cos I'm at it again
Lightning striking, and on that you can depend
They say that lightning never strikes the same place twice
Gods of thunder, sit and watch the event
-Ozzy Osbourne Lightning Strikes
The Salesforce Lightning Design System provides a library of pre-built UI elements.

"Thinking in React" is essentially the art of breaking down larger user interfaces into several smaller components.

Combined, the two provide an extremely powerful framework for building interactive user interfaces in Salesforce.
Picture
Lightning Tabs

Let's use the Lightning Tabs component as an example. After pasting the example code into a Visualforce page, a very nicely formatted navigation menu will appear in the page.
Picture
However, without Javascript, clicking on the tabs will have no effect.

To make this navigation tab interactive, we'll wrap the individual Tab components in React classes. We begin by deconstructing the Tab UI down into smaller classes that can be implemented in React.

A hierarchy of React components representing a Lightning tab navigation:
App   (The application container)
   Tabs   (A collection of tabs)
      Tab     (A single tab instance)
         Content    (Content associated with a tab)


Data

ReactJS components "react" to changes in data state. The data we're going to inject into this application is an array of Tabs, defined as follows:
    var tabList = [
      { 'index': 0, 'title': 'Item One', 'content':
        <div id="tab-scoped-0" class="slds-tabs__content slds-show" role="tabpanel">
            <h2>Item One Content</h2>
        </div>
      },
      { 'index': 1, 'title': 'Item Two', 'content':
        <div id="tab-scoped-1" class="slds-tabs__content slds-show" role="tabpanel">
            <h2>Item Two Content</h2>
        </div>
            },
      { 'index': 2, 'title': 'Item Three', 'content':
        <div id="tab-scoped-2" class="slds-tabs__content slds-show" role="tabpanel">
            <h2>Item Three Content</h2>
        </div>
      }
    ];



App

Now this is where the ReactJS framework comes into play. The App class provides the outermost container for the entire application. The state of which tab is currently selected is managed in the App class.
      
    var App = React.createClass({
      getInitialState: function () {        
        return {
          tabList: tabList,
          currentTab: 0
        };
      },
            
      changeTab: function(tab) {
        this.setState({ currentTab: tab.index });
      },
            
      render: function(){
        return(
          <div className="slds">
            <Tabs 
              currentTab={this.state.currentTab}
              tabList={this.state.tabList}
              changeTab={this.changeTab}
            />
                  {this.state.tabList[this.state.currentTab].content}
          </div>
        )
      }
    });

Tabs

The App.render() method constructs a Tabs class, which is a container for many individual tabs.
    var Tabs = React.createClass({
      handleClick: function(tab){
                this.props.changeTab(tab);
      },
            
      render: function(){
        return (
          <div className="slds-tabs--scoped">
          <ul className="slds-tabs--scoped__nav" role="tablist">
          {this.props.tabList.map(function(tab) {
            return (
              <Tab
                handleClick={this.handleClick.bind(this, tab)} 
                index={tab.index} 
                title={tab.title} 
                isCurrent={(this.props.currentTab === tab.index)}
              />
            );
          }.bind(this))}
          </ul>
          </div>
        );
      }
    });

Tab

The majority of dynamic rendering occurs in the Tab class.
      
    var Tab = React.createClass({
      handleClick: function(e){
        this.props.handleClick();
      },
            
      render: function(){
        var tabClass = "slds-tabs__item slds-text-heading--label";
        tabClass += (this.props.isCurrent ? ' slds-active' : '');
                var selected = (this.props.isCurrent ? 'true' : 'false');
                var path = '#' + encodeURIComponent( this.props.title.toLowerCase() );
                var controls = 'tab-scoped-' + this.props.index;
        return (
          <li className={tabClass} title={this.props.title} role="presentation"><a href={path} onClick={this.handleClick} role="tab" tabindex={this.props.index} aria-selected={selected} aria-controls={controls}>{this.props.title}</a></li>
        )
      }
    });

Finally, this snippet of code sets the whole interactive model in motion. It creates an instance of the <App/> class within the document <body> tag.
    
    React.render(
      <App />,
      document.body
    );

Event Handling

You'll notice that the Tab onClick event gets bubbled up through the Tabs container, and is ultimately handled by the App class.

Events are handled at the most appropriate scope in an application. In this case, the App class updates the currentTab variable and the entire DOM is automatically updated to reflect the change in state.

Also notice that no dynamic removeClass() or addClass() logic is required to re-render the tabs. This is where the real magic of React is utilized.

​React manages a virtual DOM (Document Object Model) and diff engine to automatically determine which components in the hierarchy need to be re-rendered every time there is a change in data state.

​The Whole Enchilada

Wrapping Lightning Design Studio components in React classes is a remarkably powerful and efficient combination.

An interactive navigation menu can be implemented in under 100 lines of code without writing any custom CSS or complex JS event handlers. Entire source code for this example is below.
Need a custom Salesforce application or AppExchange product developed using the latest Lightning design studio components? Contact us for details.
<apex:page showHeader="false" standardStylesheets="false" sidebar="false" applyHtmlTag="false" applyBodyTag="false" docType="html-5.0" cache="false">
<html xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <head>
  <title>App Title</title>
  <apex:stylesheet value="{!URLFOR($Resource.SLDS092, 'assets/styles/salesforce-lightning-design-system-vf.css')}" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.0/react-dom.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.0/react.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
  <script type="text/babel">
    var tabList = [
      { 'index': 0, 'title': 'Item One', 'content':
        <div id="tab-scoped-0" class="slds-tabs__content slds-show" role="tabpanel">
            <h2>Item One Content</h2>
        </div>
      },
      { 'index': 1, 'title': 'Item Two', 'content':
        <div id="tab-scoped-1" class="slds-tabs__content slds-show" role="tabpanel">
            <h2>Item Two Content</h2>
        </div>
            },
      { 'index': 2, 'title': 'Item Three', 'content':
        <div id="tab-scoped-2" class="slds-tabs__content slds-show" role="tabpanel">
            <h2>Item Three Content</h2>
        </div>
      }
    ];
      
    var App = React.createClass({
      getInitialState: function () {        
        return {
          tabList: tabList,
          currentTab: 0
        };
      },
            
      changeTab: function(tab) {
        this.setState({ currentTab: tab.index });
      },
            
      render: function(){
        return(
          <div className="slds">
            <Tabs 
              currentTab={this.state.currentTab}
              tabList={this.state.tabList}
              changeTab={this.changeTab}
            />
                  {this.state.tabList[this.state.currentTab].content}
          </div>
        )
      }
    });
      
    var Tabs = React.createClass({
      handleClick: function(tab){
                this.props.changeTab(tab);
      },
            
      render: function(){
        return (
          <div className="slds-tabs--scoped">
          <ul className="slds-tabs--scoped__nav" role="tablist">
          {this.props.tabList.map(function(tab) {
            return (
              <Tab
                handleClick={this.handleClick.bind(this, tab)} 
                index={tab.index} 
                title={tab.title} 
                isCurrent={(this.props.currentTab === tab.index)}
              />
            );
          }.bind(this))}
          </ul>
          </div>
        );
      }
    });
      
    var Tab = React.createClass({
      handleClick: function(e){
        this.props.handleClick();
      },
            
      render: function(){
        var tabClass = "slds-tabs__item slds-text-heading--label";
        tabClass += (this.props.isCurrent ? ' slds-active' : '');
                var selected = (this.props.isCurrent ? 'true' : 'false');
                var path = '#' + encodeURIComponent( this.props.title.toLowerCase() );
                var controls = 'tab-scoped-' + this.props.index;
        return (
          <li className={tabClass} title={this.props.title} role="presentation"><a href={path} onClick={this.handleClick} role="tab" tabindex={this.props.index} aria-selected={selected} aria-controls={controls}>{this.props.title}</a></li>
        )
      }
    });
    
    React.render(
      <App />,
      document.body
    );
    </script>
  </head>
  <body></body>
</html>
</apex:page>
0 Comments



Leave a Reply.

    Author

    Mike Leach is Founder and Principal Consultant at Cubic Compass; a software design and development consultancy focused on the Salesforce Force.com platform.

    Archives

    March 2017
    April 2016
    February 2016
    November 2015
    October 2015

    Categories

    All

    RSS Feed

Cubic Compass     |     PO Box 2582 El Cerrito, CA 94530 
[email protected]
  • Home
  • Navigator
  • Services
    • Subscription Services
    • Portfolio
    • CPQ Implementations
  • About
    • Contact