Here's a way to put this functionality into a custom hook that you can use in any of your functional components.
An important part that some answers are missing is to store a reference to the preloaded images in something like the window object. If you don't do this, you will probably find that the browser will re-request the images once they're actually needed despite the fact that they were already preloaded.
The first time you use this hook, a key will be added to window
called usePreloadImagesData
and it will be initialized to {}
.
Each time the hook is used in a component, a new key will be added to window.usePreloadImagesData
. The key's name is randomly generated. It is initialized to []
.
The hook takes an array of image source strings. For each image source, a new Image()
is created and added to the window.usePreloadImagesData[key]
array.
When the component is unloaded, or the array passed to the custom hook changes, window.usePreloadImagesData[key]
will be deleted to avoid a memory leak.
This example is written in Typescript.
import { useEffect } from 'react';
declare global {
interface Window {
usePreloadImagesData?: Record<symbol, unknown[]>;
}
}
export const usePreloadImages = (imageSrcs: string[]): void => {
useEffect(() => {
const key = Symbol();
window.usePreloadImagesData = window.usePreloadImagesData ?? {};
window.usePreloadImagesData[key] = [];
for (const src of imageSrcs) {
// preload the image
const img = new Image();
img.src = src;
// keep a reference to the image
window.usePreloadImagesData[key].push(img);
}
return () => {
delete window.usePreloadImagesData?.[key];
};
}, [ imageSrcs ]);
};
When using this hook, ensure that the array of image source strings passed to the hook is constant; otherwise it will cause unnecessary re-rendering. I.e., either create it outside the component or use something like React.useMemo
.
E.g.
import React from 'react';
import { usePreloadImages } from '../hooks/usePreloadImages';
import img1 from './image-1.jpg';
import img2 from './image-2.jpg';
const preload = [ img1, img2 ]; // create constant array here, outside the component
export const MyComponent: React.FC = () => {
usePreloadImages(preload); // correct
// usePreloadImages([ img1, img2 ]); // incorrect
return (
// ...
);
};