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 hereThe 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:
add a second terminal
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 directory
run 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.
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))
}
}
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
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
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
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.
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 👋
Oldest comments (2)
Hi, I wanted to kindly point out that your header image has
react
misspelled and is currently written asreat
.I wanted to draw your attention to this in case you were wanting to update as your post is gaining visibility on Twitter.
thank you for pointing that out.