Skip to content
Modus-Logo-Long-BlackCreated with Sketch.
  • Services
  • About
  • Blog
  • Partners
  • Work
  • Labs
  • Careers
  • Contact
Modus-Logo-Long-BlackCreated with Sketch.
  • Services
  • About
  • Blog
  • Partners
  • Work
  • Labs
  • Careers
  • Contact
March 27, 2018

Ext JS to React: Carousel

Application Development

This is part of the Ext JS to React blog series. You can review the code from this article on the Ext JS to React Git repo.

The carousel component, popularized in Sencha Touch and now available in Ext JS in the modern toolkit, is similar to the tab panel in that views in a card layout are shown and hidden using a navigation bar. Unlike the tab panel, carousel navigation is simplified by dropping text and icons in favor of a simple nav element like a circle with “active” styling to show which child item in the card array is in view. In addition to interacting with the nav elements you can swipe to reveal neighboring cards in a carousel view.

Note: While not a requirement for React, this article’s code examples assume you’re starting with a starter app generated by create-react-app.

React Carousel Class

Let’s look at an example of the carousel view in React. We’ll start by defining a Carousel class. First, we’ll need to install the react-swipeable-views package:

npm install --save react-swipeable-views

The react-swipeable-views package enables the animated card-swapping action as you navigate between cards using the dot indicators as well as dragging / swiping between cards. Users of the React Material UI library may recognize its use from the “Swipeable example” in the tabs demo.

import React, { Component } from 'react';
import SwipeableViews from 'react-swipeable-views';
import './Carousel.css';

class Carousel extends Component {
  static defaultProps = {
    activecard: 0,
    className: '',
    position: 'bottom'
  }

  state = {
    activecard: this.props.activecard
  }

  render () {
    let { className } = this.props;
    className = className ? ` ${className}` : '';

    const { children, position } = this.props;
    const { activecard } = this.state;
    const xPositions = ['top', 'bottom'],
          axis = xPositions.includes(position) ? 'x' : 'y';

    return (
      <div
        {...this.props}
        className = {`carousel ${position}${className}`}
      >
        <div className={`nav-strip`}>
          {React.Children.map(children, (child, i) => {
            const isActive = (i === activecard) ? 'active' : '';

            return <div
              onClick={this.onNavClick.bind(this, i)}
              className={`nav ${isActive}`}
            >
              <span className="nav-dot"></span>
            </div>;
          })}
        </div>
        <SwipeableViews
          index={activecard}
          onChangeIndex={this.onNavClick.bind(this)}
          enableMouseEvents={true}
          axis={axis}
        >
          {React.Children.map(children, (child, i) => {
            let { className } = child.props;
            className = className ? ` ${className}` : '';

            const isActive  = (i === activecard) ? ' active' : '';
            const cardProps = {
              ...child.props,
              style     : {flex: 1},
              className : ` card${isActive}${className}`,
              cardindex : i,
              activecard
            };

            return React.cloneElement(child, cardProps);
          })}
        </SwipeableViews>
      </div>
    );
  }

  onNavClick (activecard) {
    this.setState({
      activecard
    });
  }
}

export default Carousel;


React Carousel Class Explained

Above the class definition, we’re importing react-swipeable-views which we’ll use to wrap the child card items in the render method (described below). The static defaultProps property sets the defaults for various props on the Carousel. The constructor method sets the initial state and activecard property and the onNavClick method handles the nav element click that sets the activecard property which then styles the active nav element and shows the associated child card. The render method:

  • Combines any className string passed in with those added by the class
  • Collects the activecard from the component state object to inform the nav elements / cards which is currently active / visible
  • In the return:
    • Create the wrapping Carousel element that will house the nav element container and card container
    • The nav element container is added and we iterate over the child nodes (cards) passed to the Carousel to create navigation elements. The active nav element is styled as active when the cardindex matches the activecard.
    • A react-swipeable-views instance, SwipeableViews, is added to enclose the child cards passed to the Carousel. SwipeableViews enables the swiping of cards into view in addition to interacting with the nav elements.
    • We loop over the child nodes, this time calling React.cloneElement in order to add a few props like className, cardindex, and activecard to the original nodes that were passed in. The cloneElement method allows us to effectively extend the child items by taking on additional props as needed. The cloned elements are returned in an array to be the child nodes of the SwipeableViews parent. The card whose cardindex matches the activeitem is shown while the other cards are hidden using CSS rules.

React Carousel CSS

The CSS used to render the Carousel view:

.carousel {
  position: relative;
}
 
.nav-strip {
  display: flex;
  justify-content: center;
  position: absolute;
  pointer-events: none;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
}
.nav-strip + div,
.nav-strip + div .react-swipeable-view-container {
  height: 100%;
  width: 100%;
}
.carousel.top .nav-strip {
  bottom: auto;
}
.carousel.bottom .nav-strip {
  top: auto;
}
.carousel.left .nav-strip {
  right: auto;
}
.carousel.right .nav-strip {
  left: auto;
}
.carousel.top .nav-strip,
.carousel.bottom .nav-strip {
  flex-direction: row;
}
.carousel.left .nav-strip,
.carousel.right .nav-strip {
  flex-direction: column;
}
 
.react-swipeable-view-container > div {
  flex-basis: 100%;
  background: #f7f7f7;
}
 
.nav {
  text-align: center;
  cursor: pointer;
  pointer-events: all;
}
 
.carousel.top .nav,
.carousel.bottom .nav {
  padding: 12px 6px;
}
.carousel.left .nav,
.carousel.right .nav {
  padding: 6px 12px;
}
 
.nav-strip .nav-dot {
  background-color: #d2d2d2;
  border-radius: 50%;
  height: 12px;
  width: 12px;
  display: inline-block;
}
.nav-strip .nav:hover .nav-dot {
  background-color: #b5b5b5;
}
.nav-strip .nav.active .nav-dot {
  background-color: #1e8bfb;
}
 
.carousel .card {
  padding: 12px;
}
React Carousel Example
We can create a Carousel instance like:

import React, { Component } from 'react';
import Carousel from './Carousel';

class App extends Component {
  render() {
    return (
      <Carousel style={{ height: '400px', width: '600px' }}>
        <div>Content for the first panel</div>
        <div>... and the second panel</div>
      </Carousel>
    );
  }
}

export default App;


Ext JS to React: Carousel, Panel



We pass in the style prop to give the rendered component explicit dimensions. We can pass a position prop to position the navigation indicators on the “top” or “bottom”. An activecard prop can also be passed to designate the initially active card view.

Conclusion

Hopefully the example demonstrates how easy it will be to get a carousel view built for your React applications. The example is relatively basic, but gets the job done for the most common use cases. It would be fairly easy to enhance the example and allow the carousel instance to stipulate whether the carousel is oriented horizontally as shown, or vertical. However, if you’re looking for a pre-built slider, look no further than react-slick for a very performant and highly configurable carousel view.

Posted in Application Development
Share this

Mitchell Simoens

Mitchell Simoens is a Senior Front End Engineer at Modus Create. Mitchell has spent the last 10 years working with Ext JS including developing core functionality, Sencha Fiddle and (I hope your insurance covers blown minds) supporting the online community with over 40,000 posts on the Sencha Forums. Before working with Ext JS, Mitchell used Perl and PHP but loves spending time with Node JS for today's needs. When not working, you can find Mitchell relaxing with his wife and daughter, or developing his talents as an amateur furniture maker.
Follow

Related Posts

  • React Landing
    Ext JS to React: Migration to Open Source

    Worried about Migrating from Ext JS? Modus has the Answers Idera’s acquisition of Sencha has…

  • Ext JS to React: FAQ
    Ext JS to React: FAQ

    This is part of the Ext JS to React blog series. React is Facebook's breakout…

Subscribe to the Modus Newsletter

Receive the latest insights from our team each month.

modus create logo_white
  • Services
  • About
  • Partners
  • Work
  • Insights
  • Careers

© 2023 Modus. All Rights Reserved.

Privacy Policy | Accessibility Statement | Sitemap

Scroll To Top