asyncImages.tsx (1713B)
1 // First we need a type of cache to avoid creating resources for images 2 import { ImgHTMLAttributes } from "react"; 3 import { createResource, Resource } from "./resources"; 4 5 // we have already fetched in the past 6 export const cache = new Map<string, Resource<string>>(); 7 8 // then we create our loadImage function, this function receives the source 9 // of the image and returns a resource 10 export function loadImage(source: string): Resource<string> { 11 // here we start getting the resource from the cache 12 let resource = cache.get(source); 13 // and if it's there we return it immediately 14 if (resource) return resource; 15 // but if it's not we create a new resource 16 resource = createResource<string>( 17 () => 18 // in our async function we create a promise 19 new Promise((resolve, reject) => { 20 // then create a new image element 21 const img = new window.Image(); 22 // set the src to our source 23 img.src = source; 24 // and start listening for the load event to resolve the promise 25 img.addEventListener("load", () => resolve(source)); 26 // and also the error event to reject the promise 27 img.addEventListener("error", () => 28 reject(new Error(`Failed to load image ${source}`)) 29 ); 30 }) 31 ); 32 // before finishing we save the new resource in the cache 33 cache.set(source, resource); 34 // and return return it 35 return resource; 36 } 37 38 export function SuspenseImage( 39 props: ImgHTMLAttributes<HTMLImageElement> 40 ): JSX.Element { 41 loadImage(props.src ?? "undefined").read(); 42 return <img {...props} />; 43 }