import { useEffect, useState, type ReactElement } from 'react';
async function getImage(): Promise<Blob> {
// some random async code
const res = await fetch('https://picsum.photos/200');
const blob = await res.blob();
return blob;
}
export default function Image(): ReactElement {
const [src, setSrc] = useState<string | null>(null);
useEffect(() => {
getImage()
.then((blob) => {
const url = URL.createObjectURL(blob);
setSrc(url);
})
.catch((error) => {
throw error;
});
return () => {
if (src !== null) {
URL.revokeObjectURL(src);
}
};
}, [src]);
return <img src={src ?? undefined} alt="some image" />;
I want to render an image from a Blob and then revoke it after unmount. But this seems to cause the effect to run for a infinite loop. I wonder what is the proper way to do it or what’s the best practice to handle this.
>Solution :
The infinite loop in your code is caused by including src as a dependency in the useEffect dependency array. Whenever the value of src changes, the effect will be triggered again, resulting in an infinite loop.
To fix this issue, you can modify your code as follows:
import { useEffect, useState, type ReactElement } from 'react';
async function getImage(): Promise<Blob> {
// some random async code
const res = await fetch('https://picsum.photos/200');
const blob = await res.blob();
return blob;
}
export default function Image(): ReactElement {
const [src, setSrc] = useState<string | null>(null);
useEffect(() => {
let url: string | null = null;
getImage()
.then((blob) => {
url = URL.createObjectURL(blob);
setSrc(url);
})
.catch((error) => {
throw error;
});
return () => {
if (url !== null) {
URL.revokeObjectURL(url);
}
};
}, []);
return <img src={src ?? undefined} alt="some image" />;
}
In this updated code, I removed src from the dependency array of useEffect, which prevents the effect from being triggered whenever src changes. Instead, I added a local variable url inside the effect to store the URL created by URL.createObjectURL(). The cleanup function then uses the url variable to revoke the object URL.
Note that since getImage is an asynchronous function, it’s better to handle any potential errors within the effect itself rather than using catch and re-throwing the error. You can add a try/catch block around the getImage call to handle any exceptions gracefully.