Getting into Sitecore JSS, Part 2: UI Frameworks

Posted 12/05/2018 by William Hubbell

Now that you have a basic understanding of react and an environment set up to play with it, let’s do something a bit more complicated. We’re going to import a UI Component Framework to create an already-made component. There are a lot of frameworks out there for React worth looking into, and I encourage you to do so. In this case, we’ll be importing React-Bootstrap to create a Carousel.

I like the Carousel as a teaching tool because it’s simple enough to implement but complicated enough to be illuminating. The react-bootstrap carousel works differently than the standard Bootstrap carousel because it doesn’t use Jquery like regular Bootstrap components. Fortunately, it’s about as easy to implement.

In your TGHelloWorld.html file from last time, let’s add the CDN calls for React-Bootstrap in the <head>. First, we’ll add React-Bootstrap proper:

<script src="https://cdnjs.cloudflare.com/ajax/libs/react-bootstrap/0.32.4/react-bootstrap.js"></script>

Next we’ll add styling. As of now the React-Bootstrap library only supports Bootstrap version 3 instead of 4, so we’ll add the appropriate version in our <head> tag after our javascript calls:

<!-- STYLING -->
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

Finally, we need to import the Carousel component from the React-Bootstrap library. Right inside your opening <script> tag (which should read <script type=”text/babel”>), copy the following import statement:

//import statements
const Carousel = ReactBootstrap.Carousel;

This is the quick and easy way to import the carousel. We’ll do this again soon in a slightly more involved way for our disconnected JSS project.

Now we’ll actually write our components. First, here’s a basic page layout using bootstrap styling:

class TestContainer extends React.Component {
           constructor() {
               super()
               this.state = {}
           }

           render(){
               return (                 
                   <div class="container">
                       <div class="row">
                           <div class="col-xs-12">
                               <h3 class="center-block centered-text">The Carousel</h3>

                           </div>
                       </div>
                   </div>
               )
           }
       }

Next, we’ll adapt React-Bootstrap’s code for the Controlled Carousel.
Put this in the <style> tag:

.centered-text{
   width: 25%;
   text-align: center;
}

.carousel img{
   width: 100%;
}

Let’s define some data now. Our Carousel will be written to accept a JSON object containing an array of slide data. You can copy the following into your <script> tag:

//Test Data
var carouselSlides = {
           "slideObjs" : [
               {
                   "imgSrc" : 'images/grecopersianwars.png',
                   "label" : 'Greco-Persian Wars',
                   "caption" : 'The Greek World During the Persian Wars'
               },
               {
                   "imgSrc" : 'images/PeloponnesianWar.png',
                   "label" : 'The Peloponnesian War',
                   "caption" : 'Athens fought Sparta a long time ago'
               },
               {
                   "imgSrc" : 'images/PhilipIIConquest.png',
                   "label" : 'Alexanders Kingdom',
                   "caption" : 'Philip II, his father, united all of Greece, save Sparta'
               }],
       };

Obviously you’re going to need to put some image files in an “image” folder inside the same folder as your TGHelloWorld.html file. I used some maps of ancient Greece, but you can put whatever you want in there. Just make sure to change the paths or else your slides won’t render correctly.

And here’s the Carousel:

class ControlledCarousel extends React.Component {
           constructor(props, context) {
               super(props, context);

               this.handleSelect = this.handleSelect.bind(this);

               this.state = {
               index: 0,
               direction: null,
               };
           }

           handleSelect(selectedIndex, e) {
               this.setState({
               index: selectedIndex,
               direction: e.direction,
               });
           }

           render() {
            const { index, direction } = this.state;


            //slides is an array of rendered slide markup
            const slides = this.props.carouselData.slideObjs.map((slideData) =>
                   <Carousel.Item>
                       <img
                           className="d-block w-100"
                           src={slideData.imgSrc}
                           alt={slideData.label}
                       />
                       <Carousel.Caption>
                           <h3>{slideData.label}</h3>
                           <p>{slideData.caption}</p>
                       </Carousel.Caption>
                   </Carousel.Item>
               );

               return (
               <Carousel activeIndex={index} direction={direction} onSelect={this.handleSelect}>
                   {slides}
               </Carousel>
               );
           }
       }

I’ll explain a couple things about the above code. One of the things you’ll notice is that this component class has a state with properties “index” and “direction.” These are crucial to the functioning of the carousel, as they determine which slide is given the “active” class, showing that slide and hiding the others.

Another thing to notice about this code is the handleSelect function. This function actually changes the state of the carousel. Whenever the state changes in a React component, it and its children are re-rendered. This function to change the state is actually passed down to each slide child, and is triggered whenever the carousel arrows or indicators are clicked. This technique of storing a state in a parent component and passing a function to change that state through props to child components is called “lifting state up,” and it’s crucial to understanding react.

The last thing to notice here is the way data is assigned to slide items. In this example we’re iterating through an array of slide data (“slideObjs”) and assigning each object and its respective data to slide markup. In Sitecore MVC you would most likely do this with a LINQ statement or a for loop. Here we’re using a .map() function, which works similarly to a LINQ statement.

Finally, let’s put it all together. Change your TestContainer component render method to reflect the following, adding a reference to the Carousel component and the data we defined after the <h3> tag:

render(){
  return (
      <div class="container">
         <div class="row">
            <div class="col-xs-12">
               <h3 class="center-block centered-text">The Carousel</h3>
               <ControlledCarousel carouselData={carouselSlides}/>
            </div>
         </div>
      </div>
  )}

Save, then open/refresh TGHelloWorld.html in your browser. You should now have a functioning bootstrap carousel!

And just in case you got lost somewhere along the way, here’s all of the code for TGHelloWorld.html:

<!DOCTYPE html>
<html>

<head>
   <meta charset="UTF-8">
   <title>React Demo for TechGuilds</title>
   <!-- JS -->
   <script crossorigin src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
   <script crossorigin src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
   <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.js"></script>
   <script src="https://cdnjs.cloudflare.com/ajax/libs/react-bootstrap/0.32.4/react-bootstrap.js"></script>

   <!-- STYLING -->
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
       crossorigin="anonymous">
</head>

<body>
   <style>
       .centered-text{
           width: 25%;
           text-align: center;
       }

       .carousel img{
           width: 100%;
       }
   </style>
   <script type="text/babel">
       //import statements
       const Carousel = ReactBootstrap.Carousel;

       //Test Data
       var carouselSlides = {
           "slideObjs" : [
               {
                   "imgSrc" : 'images/grecopersianwars.png',
                   "label" : 'Greco-Persian Wars',
                   "caption" : 'The Greek World During the Persian Wars'
               },
               {
                   "imgSrc" : 'images/PeloponnesianWar.png',
                   "label" : 'The Peloponnesian War',
                   "caption" : 'Athens fought Sparta a long time ago'
               },
               {
                   "imgSrc" : 'images/PhilipIIConquest.png',
                   "label" : 'Alexanders Kingdom',
                   "caption" : 'Philip II, his father, united all of Greece, save Sparta'
               }],
           "title" : "Ancient Greece"
       };

       class ControlledCarousel extends React.Component {
           constructor(props, context) {
               super(props, context);

               this.handleSelect = this.handleSelect.bind(this);

               this.state = {
               index: 0,
               direction: null,
               };
           }

           handleSelect(selectedIndex, e) {
               this.setState({
               index: selectedIndex,
               direction: e.direction,
               });
           }

           render() {
               const { index, direction } = this.state;

               const slides = this.props.carouselData.slideObjs.map((slideData) =>
                   <Carousel.Item>
                       <img
                           className="d-block w-100"
                           src={slideData.imgSrc}
                           alt={slideData.label}
                       />
                       <Carousel.Caption>
                           <h3>{slideData.label}</h3>
                           <p>{slideData.caption}</p>
                       </Carousel.Caption>
                   </Carousel.Item>
               );

               return (
               <Carousel activeIndex={index} direction={direction} onSelect={this.handleSelect}>
                   {slides}
               </Carousel>
               );
           }
       }

       class TestContainer extends React.Component {
          constructor() {
              super()
              this.state = {}
          }

          render(){
              return (
                  <div class="container">
                      <div class="row">
                          <div class="col-xs-12">
                              <h3 class="center-block centered-text">The Carousel</h3>
                              <ControlledCarousel carouselData={carouselSlides}/>
                          </div>
                      </div>
                  </div>
              )
          }
      }

       ReactDOM.render(<TestContainer/>, document.getElementById('anchor-div'));
   </script>
   <div id="anchor-div"></div>
</body>

</html>

To Be Continued...

By now you should understand not just the basics of React development, but how to render something useful. The bootstrap carousel is just one of many components that you can import from UI frameworks. I encourage you to experiment with others so you can get the most out of React.

Next time, we'll finally get into JSS, setting up a disconnected project and importing our Carousel and its logic. See you then! For now, please leave a comment if you have any questions, suggestions, or clarifications you'd like to share.

Add your comment