Problem
We often times reach a use case where the browser takes a lot of time to load an image and you can't really do anything but wait while the image is being loaded the UI looks broken for the end user and then suddenly the images show up to add up to this if you didn't handle a fixed side for the image container the UI elements might shift after the image is loaded which would be a very bad user experience.
A Few Reasons why the image might take longer to load.
- unoptimized images [ higher resolution images ]
- slow internet connection
Here's an example.
In the above video, you might have observed there was a significant time delay for the image to load and the text was initially at the top and was pushed down after the image was loaded.
This issue could ruin the entire UI of your application if there were more images in the application.
To, fix this issue let's write a hook -> useImage.
The main objective here is to get a loading/error/loaded
state so that we can show a loader or maybe a shimmer effect loader.
Solution
useImage({ src })
// Image Status
const imageStatus = {
LOADING: "LOADING",
LOADED: "LOADED",
FAILED: "FAILED"
};
In the above code snippet,
- we are making an image object using the Image constructor.
- we're listening to
onload
event which fires immediately after the browser loads the object. andonerror
event which fires when an error occurs during object loading. - we have a ref which will contain the actual value of the image loaded where we'll find some important properties like the image's actual height and width ( property name: naturalHeight, naturalWidth).
Usage
Workflow
Working Demo
Improvements
Now, at this point, we've achieved our goal of handling the loading/loaded/error states of the image.
But this way of handling the status is a bit tedious. Let's improve this.
Simple Way: Write a wrapper for the Image component that internally handles image states and more over we can customize by passing them as props the loader/error placeholders.