Improve Image loading using Suspense and SWR
This post assumes that you are somewhat familiar with the Suspense API and the SWR library, as it will not cover these two things in much detail.
In case you are not familiar with these two terms, here is a short summary:
- SWR (stale-while-revalidate) is a strategy that first returns data from the cache (stale), then sends a fetch request (revalidate), and finally provides up-to-date data.
<Suspense>
component allows you to display a fallback until its children have finished loading.
Let's say we want to create a simple app that displays images of all available Dota 2 heroes:
This is the result:
Not great, right? This will occur on the first load of the app because the images are not cached yet, and we are only displaying a spinner until the data becomes available. Currently, we do not have any logic that listens for image loading. It's time to fix this behavior and use Suspense for image loading as well.
This component may appear overwhelming at first glance, but let's break it down and explain what is happening.
Type definition
First we define a type called Cache
which is a record of string keys and values that are either boolean
or Promise<void>
.
The ImgCache
type is then defined as an object with a __cache
property of type Cache
, and a read method that takes a src
parameter of type keyof Cache
(a string that is a key in the Cache object) and returns a boolean
or Promise<void>
value.
The SuspenseImgProps
type is then defined as an object with the properties src
, alt
, height
, and key
, all of which are required. src
is a string representing the source URL of the image, alt
is a string representing the alternative text for the image, height
is a number representing
the height of the image in pixels, and key
is a number representing a unique identifier for the image.
imgCache object
The imgCache
object is then defined as an instance of ImgCache
.
It has a __cache
property initialized as an empty object and a read method that takes a src
parameter.
The method first checks if the src
key is not present in the __cache
object.
If it is not, the method creates a new Promise
that resolves with a value of true when the image has finished loading.
The Promise
is assigned as the value for the src key in the __cache
object.
If the src
key is present in the __cache
object and its value is a Promise
, the method throws the Promise
.
If the src
key is present in the __cache
object and its value is not a Promise
, the method returns the value.
SuspenseImg
Finally, the SuspenseImg
functional component is defined as an arrow function that takes a single props parameter of type SuspenseImgProps
.
It calls the read
method of the imgCache
object with the src
prop as an argument, and then returns an img
element with the src
, alt
, and other props specified in the props object.
Type definition
First we define a type called Cache
which is a record of string keys and values that are either boolean
or Promise<void>
.
The ImgCache
type is then defined as an object with a __cache
property of type Cache
, and a read method that takes a src
parameter of type keyof Cache
(a string that is a key in the Cache object) and returns a boolean
or Promise<void>
value.
The SuspenseImgProps
type is then defined as an object with the properties src
, alt
, height
, and key
, all of which are required. src
is a string representing the source URL of the image, alt
is a string representing the alternative text for the image, height
is a number representing
the height of the image in pixels, and key
is a number representing a unique identifier for the image.
imgCache object
The imgCache
object is then defined as an instance of ImgCache
.
It has a __cache
property initialized as an empty object and a read method that takes a src
parameter.
The method first checks if the src
key is not present in the __cache
object.
If it is not, the method creates a new Promise
that resolves with a value of true when the image has finished loading.
The Promise
is assigned as the value for the src key in the __cache
object.
If the src
key is present in the __cache
object and its value is a Promise
, the method throws the Promise
.
If the src
key is present in the __cache
object and its value is not a Promise
, the method returns the value.
SuspenseImg
Finally, the SuspenseImg
functional component is defined as an arrow function that takes a single props parameter of type SuspenseImgProps
.
It calls the read
method of the imgCache
object with the src
prop as an argument, and then returns an img
element with the src
, alt
, and other props specified in the props object.
In the end, we can import the SuspenseImg
component and replace the img
tag with SuspenseImg
. This is the final result: