bg-img

    Created by Everistus Akpabio
    Last Updated on June 24, 2020

    # How to Create a LetSuspense Component in React

    Featured Image

    Introduction

    In this article, you will learn how to create a useful component using Composition in React to present placeholders on your page while data is being fetched in the background; When your content is available, it will simply swap the placeholder for the actual data.

    What you need to know

    • Some React experience
    • Knowledge of React hooks (useEffect & useState)

    Why do we need this?

    We’re used to indeterminate spinners in web design because they’re quick and easy to implement. A better approach to user experience would be to let the loader simulate the expected content; Most developers use skeleton loaders.

    YouTubeLinkedIn
    YouTube Skeleton LinkedIn Skeleton

    In summary, this component will help us:

    1. Improve the user’s visual experience.
    2. Write less code since our loader logic will be encapsulated and re-used in a declarative manner.

    Setting up the Application

    This tutorial focuses on creating a LetSuspense component so a simple demo has already been created. You can find a link to the source code here, in addition, you can also have a look at the live demo.

    //App.js
    function App() {
      const [movies, setMovies] = useState([]);
      const [query, setQuery] = useState('');
    
      const handleSearch = (query) => {
        setQuery(query);
      };
    
      useEffect(() => {
        fetchData();
      }, [query]);
    
      const fetchData = async () => {
        const result = await fakeApi(query);
        setMovies(result);
      };
      return (
        <div className="App">
          <div className="container">
            <h2 style={{ textAlign: 'center', color: '#fff' }}>Vuetube</h2>
            <SearchBar query={query} handleSearch={handleSearch} />
            <div className="card-container">
              {movies.map(({ id, title, img, desc }) => (
                <DataCard key={id} title={title} img={img} desc={desc} /> // render card
              ))}
            </div>
          </div>
        </div>
      );
    }

    Here is a snippet of the App.js file, at this stage it just fetches data from the fakeApi and renders the data, no loader is displayed.

    Creating the LetSuspense Component

    The LetSuspense is quite simple, it needs to accept a placeholder component, the actual component and a condition. It should render the placeholder as long as the condition is not met and the actual component when the condition is met.

    I’ll define a few more properties to enhance the LetSuspense.

    Properties

    PropertyDescription
    condition(bool) [false] Determines when components should be rendered
    placeholder(React.Component) [undef] A React component that will be rendered as long as the condition is false
    children(JSX) [undef] The actual component(s) that will be rendered when the condition is true
    multiplier(Number) [1] The number of placeholders to be rendered
    initialDelay(Number) [0] Minimum time in milliseconds before a component is rendered
    checkOnce(bool) [false] Checks the condition just once, useful for data that doesn’t need to be re-rendered even if the condition changes.

    The Code

    First we need to import React and a few hooks and also destructure our expected props in the parameter; this helps with code completion.

    //LetSuspense.js
    import React, { Fragment, useEffect, useState } from 'react';
    
    export default function LetSuspense({
      condition,
      placeholder: Placeholder,
      multiplier,
      initialDelay,
      checkOnce,
      children,
    }) {
      return <Fragment></Fragment>;
    }

    Next we need to manage states; The condition (placeholder or actual component) and whether the condition has been checked.

    //LetSuspense.js
    ...;
        const [component, setComponent] = useState([]);
        const [isChecked, setIsChecked] = useState(false);
    
        return (
            <Fragment>
            </Fragment>
        );

    It’s time to utilize useEffect, useEffect has a 2nd argument which takes an array and causes the component to re-render, we should add both the condition & children props to that array so whenever they change in the parent component, there will be updated here as well.

    Note: children here refers to a special props in React, props.children, which means that anything passed into our <LetSuspense> tag will be available and implicitly referenced as children.

    //LetSuspense.js
    useEffect(() => {
      // make sure the condition check happens just once if checkOnce is true
      if (checkOnce && isChecked) {
        setComponent([children]);
        return;
      }
      let delay = initialDelay || 0;
      let delayedTimeout = null;
      // render actual component when condition is true
      if (condition) {
        if (initialDelay) {
          delayedTimeout = setTimeout(() => {
            setComponent([children]);
          }, delay);
        } else {
          setComponent([children]);
        }
        setIsChecked(true);
      } else {
        // render placeholder as long as condition is false
        let tempComponent = [];
        multiplier = multiplier || 1;
        for (let i = 0; i < multiplier; i++) {
          tempComponent.push(<Placeholder key={i} />);
        }
        setComponent(tempComponent);
      }
      // cleanup side effects
      return () => {
        if (delayedTimeout) {
          clearTimeout(delayedTimeout);
        }
      };
    }, [condition, children]);
    return <Fragment></Fragment>;

    The useEffect will populate the component array with the placeholders or actual component that need to be rendered. We can map that array to return a new component.

    //LetSuspense.js
    ...;
      return (
        <Fragment>
          {component.map((component, index) => (
            <Fragment key={index}>{component} </Fragment>
          ))}
        </Fragment>
      );

    Great! that’s all you need to create your LetSuspense Component, let’s import that to our App.js file and see how we can wrap it around our Datacard.

    //App.js
    const fetchData = async () => {
      const result = await fakeApi(query);
      setMovies(result);
    };
    
    return (
      <div className="App">
        <div className="container">
          <h2 style={{ textAlign: 'center', color: '#fff' }}>Vuetube</h2>
          <SearchBar query={query} handleSearch={handleSearch} />
          <div className="card-container">
            {/* LetSuspense Added, it encapsulates its children/actual component*/}
            <LetSuspense
              condition={movies.length > 0}
              placeholder={Placeholder}
              multiplier={3}
              initialDelay={200}
            >
              {movies.map(({ id, title, img, desc }) => (
                <DataCard key={id} title={title} img={img} desc={desc} /> //render card
              ))}
            </LetSuspense>
          </div>
        </div>
      </div>
    );

    Final Notes

    Hopefully, this tutorial showed you how to create a useful component that will come in handy for most of your projects.

    Try adding some new props to enhance your LetSuspense like a timeout and an alternative placeholder for when it times out (could just be a text with a message or a button to retry fetching data).

    This article was written by Everistus Akpabio, you can visit his website and Github profile.