Part 6: Build a CSS-in-JS React App with Styled Components and Priceline Design System

@creatrixity · 2018-06-13 23:39 · utopian-io

FireLiners Skeletal Loading Screen

Users don't like surprises.

As a matter of fact, most people are not very comfortable with sudden, unexpected occurrences. This is due to the association of abrupt events and their startling natures with uncertainty. Instinctively, we tend to avoid jarring, unexpected changes to our natural environments.

To get a better picture, carefully observe the plot sequence in movie releases from the horror genre. You could easily notice a trend; events with the most shock value are typically abrupt introductions of agents (either malevolent or benign) into the set. It's a well known physical principle that linear changes do not occur in nature, rather natural mutations are seemingly eased or graduated.

Since our ideal interaction experiences with digital devices tend to be an approximation of our interaction with everyday items, it is completely rational that we try to model the physical world within our applications. This means the reduction of "surprises" or unexpected occurrences to a bare minimum.

Typically, most jarring and disorienting changes within user interfaces occur when content is being rendered. First of all, when loading a web resource there is the infamous blank white screen followed by the abrupt appearance of content. While this ought to be really disorienting, hedonic adaptation allows us to simply gloss it over and get on with our business. Modern interfaces designed by some of the most successful tech enterprises work within the constraints of principles governing the user's mental interaction model.

Prime among the principles governing the interaction model, is the principle of progressive content rendering. This simply means the appearance of content is first simulated before said content is available to avoid the flash of a blank white screen. Content appearance must first be simulated by displaying visual placeholder content prior to the requested content being available.

Last time around we explored the use of infinite scrolling techniques to improve perceived responsiveness. Today, we will explore skeletal content placeholder screens as a technique for improving perceived app response times. We will also look at action notifications as an effective feedback mechanism.

Disclaimer:

This tutorial may be pretty challenging for a first time reader of this series. I sincerely recommend that visitors get at least a cursory gaze at the previous articles in this series for easier comprehension. Links to the previous articles are available at the bottom of the this tutorial.

Repository

React Github Repository

Tutorial Repository

FireLiners Github Repository

Difficulty

  • Advanced

What Will I Learn?

By the time we get to the end of this tutorial, you should be able to apply these techniques to your React apps:

  • Reducing user attrition by utilizing layout placeholders or skeletal screen.
  • Simulating production level constraints with asynchronous function resolution.
  • Leveraging PropTypes for reusable React components.
  • Keeping users engrossed and interacting with our app via action notifications as a feedback mechanism.
  • Introducing micro-interactions for greater user engagement.

Requirements

Brief Introduction.

In the brief ramble above, we got a clear picture of the problem. We also clearly outlined the techniques we hope to use to address said problems. We'll now take a look at skeletal loading screens and what they entail.

Skeletal Loading Screens:

Skeletal loading screens are an anticipatory design technique used to suggest a visual structure for content while it's still loading. Skeletal loading screens are fairly commonplace and are currently employed by Facebook and YouTube for progressive rendering in their application user interfaces.

How does skeletal loading work? Its working principle is fairly easy to wrap your head around. Skeletal loading works by detecting the loading state of your app: if your app is currently loading, the skeletal placeholders (usually

) elements with some styling attached are displayed.

You can generate a skeletal screen by first observing the fully loaded data state and then attempting to mimic it by using elements styled to resemble the content structure.

Below are some apps with skeletal loading screens:

  • Sofascores Skeletal UI

SofaScore UI.png

  • Twitter Skeletal

Twitter Skeletal Loading

Adding Skeletal Screens to FireLiners

You can add skeletal screen functionality by designing it as a React component and toggling it by checking the application's loading state.

To save on time, we'll be using the awesome dvtng/react-loading-skeleton package. You can get it installed by running

npm install react-loading-skeleton --save

With this package installed, let's examine our structure once more

  • fire-liners/

    • config/...
    • node_modules/...
    • public/...
    • scripts/...
    • src/
      • assets/
        • data/
          • authors.json
          • liners.json
        • img/...
      • components/
        • Header/
          • index.js
          • logo.svg
      • containers/
        • App/
          • App.test.js
          • index.js
          • constants.js
          • reducer.js
      • redux/
        • reducerInjector.js
        • reducers.js
        • sagas.js
        • store.js
      • screens/
        • AddLine /
          • index.js
          • actions.js
        • Home /
          • index.js
          • constants.js
          • actions.js
        • Loading /
          • index.js
      • services/
        • DataService/
          • index.js
      • index.js
      • registerServiceWorker.js
    • package.json

We'd like to add some skeletal loading functionality to our Home screen. To do this, we'll leverage the react-loading-skeleton package and a bit of Boolean toggling logic. Opening up the Home class at src/screens/Home/index.js we'll be making some changes. In the constructor method, we'll add a state Boolean property called isLoadingLiners and we'll set it to true by default. This means that whenever we start the app, we understand that our liners are yet to load.

    constructor(props) {
        this.state = {
            linersSetIndex: 0,
            hasMoreItems: true,
            linersTotal: 0,

            // we'll use this to track the current loading progress of our liners.
           isLoadingLiners: true
        }
     }

Next, we'd like our app to fetch liners whenever it runs. We do this by calling the this.props.fetchLiners method. After we fetch the liners, we'd like to wait for about 3500 milliseconds before we switch off the loading state. We do this by calling this.setState and setting the isLoadingLiners to false within a setTimeout function effectively letting our app now we're done with loading. This is done to ensure that we are able to reasonably simulate production environments.

Since we are loading our data from a JSON file, loading is nearly instantaneous however this is not the case on production environments as only the fastest 4G networks can guarantee near instantaneous loading. So, we use the setTimeout call to simulate production levels of uncertainty.

    componentDidMount() {
        // Previous code here...
        this.props.fetchLiners({
            linersSetIndex: this.state.linersSetIndex
        })

        setTimeout(() => {
            this.setState({
                isLoadingLiners: false
            })
        }, 3500)
    }

Also, we'll be applying the same technique to our fetchMoreData method that is called by our Infinite Scroll component whenever we need to fetch more liners. In this method, we start of by setting the isLoadingLiners local state property to true to signify that we're loading liners. Within the setTimeout call, we wait for 1500 milliseconds and then we send a request to fetch liners. We also switch off loading by setting isLoadingLiners to false.

    fetchMoreData = () => {
      // a fake async api call like which sends
      // 20 more records in 1.5 secs
      this.setState({
          isLoadingLiners: true
      })

      setTimeout(() => {

          if (this.state.hasMoreItems) {
               // previous code
              this.props.fetchLiners({
                  linersSetIndex: this.state.linersSetIndex
              })

              this.setState({
                  isLoadingLiners: false
              })
          }
      }, 1500);
    };

Great! We are now able to switch our loading on and off and even simulate loading using the asynchronous setTimeout call. Next, we need to fix our component to enable it work with skeletal loading properly. We'll be introducing the isLoading attribute to the Feed component props in our render method. We'll set the value to be equal to the isLoadingLiners state Boolean. This will help us communicate to the Feed component the current state of our loading.

                    

It's now time to work on our Feed component class. We'll be enforcing proptypes within our Feed component. To help us do this, we'll simply need to import the PropTypes class from the prop-types package. We'll also be importing the Skeleton component from react-loading-skeleton.

import PropTypes from 'prop-types';
import Skeleton from 'react-loading-skeleton';

HomeScreen Author Image

We'll also be making changes to the JSX markup we have in the render method. The markup below is responsible for rendering the photo of the author. Thankfully, we had enough sense to make a placeholder-like Circle component. We'd like to only show a simple gray circle if the liner data is not fully available. To do this, we run a check that only shows the image if:

  • props.isLoading is false. This stops us from showing the image if the isLoading property value is false.

  • The number of liners is greater than or equal to the index of the current liner under evaluation: Let's make it a little easier to understand. We'll assume we've got five liners at first and the current liner is the sixth liner. Since six is greater than five, the image is not rendered at first, however when the liners are updated to ten, then the image is rendered.

                                
                                  {!props.isLoading && (props.liners.length) >= index ? (
                                      getLinerAuthor(liner, props.authors).photo &&
                                        
                                    ) : null
                                  }
                                

We also apply the above to the body of the liner. Here, we resort to showing a skeleton composed of three full width lines to represent the liners if our app is still loading liners. We can generate the number of lines required by simply passing in a value to the count prop.

                                
                                    {!props.isLoading && ((props.liners.length) >= index) ? liner.body : }
                                

We also do the same for the author name. This time however, we don't want a very long line (names are usually short) so we generate a line that is 100px long by passing 100 to the width prop. We also place it within a Flex component with the flex direction set to row-reverse as this helps us put the line to the right.

                                {
                                    !props.isLoading && (props.liners.length) >= index ? (
                                        
                                            {liner.author}
                                        
                                    ) : (
                                        
                                            
                                        
                                    )
                                }

Awesome! We're done with our mini skeletal screen. You can see skeletal loading below.

FireLiners Skeletal Loading Screen

Action Notifications as a Feedback Mechanism.

Before we proceed, let's step back a little and role play. In today's scenario, you are an impatient shopper with a really long wish list. You visit the nearby mart and you fill your trolley. You pull up to the attendant for a checkout and you ask, "Hi, I'd like to pay for my stuff." surprisingly you get no response. You keep going on but yet again no acknowledgement. You storm out of the establishment frustrated. The previous story displayed is analogous to the scenario that plays out when you offer no indicators to your users when they perform an action.

On optimum user experience is all-encompassing as it involves you making sure all sources of pain for your users are eliminated. A typical pain point for users is the process of form submission especially asynchronous form submission. The pain involved usually emanates from the uncertainty generated by some actions. For example, if you attempt to submit a form on the web, you usually expect some form of interaction or feedback to enable you know if your form submission is being processed or if it has failed.

We'll be adding subtle action notifications as feedback for our app. These notifications will be twofold:

  1. Small interactions for user triggered events.
  2. Success notifications for users whenever an action is completed.

We'll be adding an interaction that allows us to signal to the user that their action is underway. We'll be adding this interaction to the button with which we'll submit our liners. To do this, we'll display a little loading indicator along with descriptive text when we attempt a submit. We'll also be adding micro-interactions for our application's loading screens. We'll grab the react-spinners dependency and be on our way.

npm install --save react-spinners

We'll first start by adding some loading spinners to the loading screen. Open up src/screens/Loading/index.js and we'll get to work. We'll simply import the SyncLoader spinner type that comes with a really cool bouncing animation. We'll then position the sync loader in the middle of our loading screen. How cool is that?

import { SyncLoader } from 'react-spinners';

const AppLoadingScreen = props => (
    
        

        Whipping up Awesomeness...
    
)

We'll also add this micro-interaction to our Infinite Scroll loader. We can do this by editing the Home Screen class available at src/screens/Home/index.js and modifying the Infinite Scroll instantiation code to use the SyncLoader instead of text. We update the style for the Infinite Scroll list to prevent annoying scrollbars from popping up. We then simply instantiate our SyncLoader where our text used to be.

  
                
            
        }
        endMessage={
          

Homie, you done seen all the liners we got.

} > //...other code

Creating an Action Indicator for our Submit Action.

We'll now attempt to create an action indicator for our Submit button. We'll hop over to our src/screens/AddLine/index.js and make some changes. First of all, we'll use the ScaleLoader now available thanks to react-spinners and we'll also add a new state property called isSubmitting. We'll use this Boolean state property to keep track of our submission. By default, we set it to false but we'll make it true when we make a submission. We'll also simulate production level constraints by using setTimeout before adding our liner.

import { ScaleLoader } from 'react-spinners';

class AddLine extends Component {
    constructor (props) {
        super(props);
        this.state = {
            // ...previous code
            isSubmitting: false
        }
    }
}

Next, we modify our RedButton (our submit button), We disable the button once a submission action is in progress. This keeps users from making double submissions. We also check if we're currently making a submission and if we are currently doing so, we show our ScaleLoader along with some descriptive text. If we are not currently submitting, we simply show the regular text.

    
        {
            !this.state.isSubmitting ?
            Save and go back:
            
                Submitting

                
            
        }
    

In our handleSubmit event handler, we start of by setting isSubmitting to true to set the stage. We then wrap our submission code within the setTimeout function to simulate production level environments. Finally, we also introduce a method called this.props.addNotification that will be

#utopian-io #tutorials #programming #technology #coding
Payout: 0.000 HBD
Votes: 189
More interactions (upvote, reblog, reply) coming soon.