DEV Community

Cover image for Learn how to use React Suspense for data fetching (part 2)
Derick Zihalirwa
Derick Zihalirwa

Posted on • Updated on

Learn how to use React Suspense for data fetching (part 2)

Hello 👋,

In the first part, we explained React Suspense APi, what it does and how it works in conjunction with lazy components.

In this last part, we will see how to use React Suspense when it comes to fetching data. While waiting for the response to be available, the user needs to see something on his screen and React Suspense does it better, by providing a fallback that displays something on the screen to let the user know that there is a process loading in the background.

Prerequisites

  • If you didn't read the first part of this series click here to read it.

  • A basic understanding of JSON server, i have an article that will give you a better understanding of the concept with some examples.
    you can read it here

  • The code for this article can be found here.

First, you need to clone the repo in your local machine,
next, Open the repo in your text editor (in my case I'm using VScode).
We have two folders: web-client and server.
Open up your terminal inside vscode: Control + J for windows users and Command + J for mac users.
you should be in this directory:

Folder Structure
add a second terminal
Two terminals
To install all the dependencies, in the first terminal navigate to web-client directory then run npm install, and do the same in the server directory but do it in a second terminal.

When all the dependencies are installed, in the server directoryrun the command npm run serve-json, aport 7000 will be running on your machine and npm run dev in web-client. Next go to localhost:3000 in your browser to access the website.

Running the servers
Now let's fetch data from the fake REST API which is running on our localhost:7000/data.

to do that let's create a new file inside web-client/src/page/FiciGame/
name it fetchFici.js

past the code below inside:

const fetchFici = (search) => {
    return fetch(`http://localhost:7000/data?q=${search}`)
        .then(res => res.json())
        .catch(err => console.log(err))
}

const wrapPromise = (promise) => {
    let status = 'pending';
    let result = '';
    let suspender = promise.then(
        r => {
            status = 'success';
            result = r;
        },
        e => {
            status = 'error';
            result = e;
        }
    );
    return {
        read() {
            if (status === 'pending') {
                throw suspender;
            } else if (status === 'error') {
                throw result;
            }
            return result;
        }
    }
}

export const createResource = (search) => {
    return {
        data: wrapPromise(fetchFici(search))
    }
}

Enter fullscreen mode Exit fullscreen mode

Illumination:

the first function fetchFici fetches data according to the parameter passed in.
http://localhost:7000/data?q={<params>}.

To have this work we are going to create a function that going to take a promise as a parameter, inside we create a string variable called status with 'pending' as a default value. This will keep track of whether a promise is completed or whether is loading or got an error.
The next line is a variable that going to wait for the promise.

At the bottom, we return a function that is going to read and check the status.
React Suspense expects us to throw a promise so it can catch it.

Last we export a function that returns an object, inside that object we passe fetchFici() which is going to return a promise that we are wrapping inside wrapPromise making sure it fits the Suspense API.

Once we have this setup we can reuse it.

import React, { Suspense, useState } from 'react'
import Spinner from '../../layout/spinner';
import { createResource } from './fetchFici';
const FiciGame = () => {
    const [fici, setFici] = useState('Fire')
    const resource = createResource(fici);

    return (
        <div className='border rounded-md bg-zinc-800 border-white p-4'>
            <p className='text-center text-3xl font-mono'>Fici Game</p>
            <Suspense fallback={<Spinner />}>
             //Component to be add
            </Suspense>
            <ul className='flex justify-center gap-2 items-center'>
                <li onClick={() => setFici('Fire')} className='p-4 border hover:cursor-pointer text-2xl shadow-lg  bg-gray-900 border-white'>Fire</li>
                <li onClick={() => setFici('Rock')} className='p-4 border hover:cursor-pointer text-2xl shadow-lg  bg-gray-900 border-white'>Rock</li>
                <li onClick={() => setFici('Water')} className='p-4 border hover:cursor-pointer text-2xl shadow-lg  bg-gray-900 border-white'>Water</li>
                <li onClick={() => setFici('Air')} className='p-4 border hover:cursor-pointer text-2xl shadow-lg  bg-gray-900 border-white'>Air</li>
            </ul>
        </div>
    )
}

export default FiciGame
Enter fullscreen mode Exit fullscreen mode

Illumination:

Inside index.jsx we create a useState that takes a string value.
We then set the value according to the <li> tag that will be pressed.

then we import creacteRessource() which we save in resource, inside we pass the string got from fici.

Next, we import suspense component from React, we pass a fallback function which will be displayed while the data are fetching in the background.
Inside the Suspense api we are going to create a component (i called it Screen) that will render after the process is done.

Now let's create that component.
Inside web-client/src/page/FiciGame/ create a file called screen.jsx, inside that file type this code:

import React from 'react'

const Screen = (props) => {
    const { resource } = props;
    const ficiData = resource.data.read()
    const ficiName = ficiData[0].name
    const ficiSymbol = ficiData[0].symbol

    return (
        <div className='border bg-gray-900 flex flex-col space-y-4 justify-center items-center border-white h-80 m-4'>
            <p className="p-4 font-mono text-lg">{ficiName}</p>
            <span className='text-9xl shadow-2xl rounded-full'>{ficiSymbol}</span>
        </div>
    )
}

export default Screen
Enter fullscreen mode Exit fullscreen mode

We are calling ressource.data.read(); if it still loading, it will throw a promise which is going to be caught by suspense component and it's going to display a loading indicator otherwise we got our data and display it.

Go back to index.jsx and import Screen.jsx and place it inside Suspense component then save all your files.

import Screen from './screen';

const FiciGame = () => {
    return (
[...]
            <Suspense fallback={<Spinner />}>
                <Screen resource={resource} />
            </Suspense>
[...]
    )
}

export default FiciGame
Enter fullscreen mode Exit fullscreen mode

Test

To test this, go to your browser open up your devtool in the network tab, on the top navigate to network and select "No throttling" choose fast 3G then refresh the page.
Testing

Congratulation you're now a master of using the Suspense APi and lazy loading component, don't hesitate to try it out. It seems slightly more complex but with more practice, you will get the hang of it.

Don't forget to leave a comment or chat with me on Twitter.

See you 👋

Top comments (2)

Collapse
 
mannyistyping profile image
Manny

Hi, I wanted to kindly point out that your header image has react misspelled and is currently written as reat.

I wanted to draw your attention to this in case you were wanting to update as your post is gaining visibility on Twitter.

Image description

Collapse
 
derick1530 profile image
Derick Zihalirwa

thank you for pointing that out.