前言
我在使用cloudinary(一个云图像/视频平台)的时候发现一个潜在问题:有时候获取图片地址的时候初次获取是无法正确获取到的,必须刷新一次或多次才能正确获取到图片,但是浏览器只会加载一次图片,这就会导致图片即使存在也无法正确加载从而被备用图片替换。
补充一下我使用cloudinary的方式:通过cloudinary提供的上传组件实现图片上传,在图片上传的同时记录下当前上传图片的云地址并存储到数据库,每次使用云图片的时候只需要调用这个地址。
正文
下面是个简单的例子,主要关注src
部分,user?.image
就是数据库中存储的云图片地址,当我们没有拿到的时候就用"/images/person.jpg"
替换。
typescript
<img
ref={imageRef}
src={user?.image || "/images/person.jpg"}
alt="image"
className="w-8 h-8 rounded-full mt-[2px]"
/>
那么如何解决前言中的问题呢?
typescript
const imageRef = useRef<HTMLImageElement | null>(null);
const handleImageError = (event: React.SyntheticEvent<HTMLImageElement, Event>) => {
const imgElement = event.currentTarget;
imgElement.src = user?.image;
};
return (
<img
ref={imageRef}
src={user?.image || "/images/person.jpg"}
alt="image"
className="w-8 h-8 rounded-full mt-[2px]"
onError={handleImageError}
/>
);
这段代码中我们给onError
绑定了handleImageError
事件,作用就是当user?.image
图片获取错误时都会触发onError
事件从而多次加载图片,这样我们就可以避免初次加载无法拿到图片的情况。
这样就结束了吗?当然没有,当user?.image
一直无法正确获取就会造成无限循环,所以我们要在其基础上进行循环处理:
typescript
const imageRef = useRef<HTMLImageElement | null>(null);
const timerRef = useRef<NodeJS.Timeout | null>(null); // 用于存储定时器
const handleImageError = (event: React.SyntheticEvent<HTMLImageElement, Event>) => {
const imgElement = event.currentTarget;
// 如果定时器已存在,说明在2s等待时间内
if (timerRef.current) {
// 继续尝试原来的操作
imgElement.src = user?.image;
return;
}
// 设置2s定时器
timerRef.current = setTimeout(() => {
// 2s后直接使用备用地址
if (imgElement) {
imgElement.src = "/images/person.jpg";
}
// 清除定时器引用
timerRef.current = null;
}, 2000);
// 首次错误时也执行原操作
imgElement.src = user?.image;
};
// 组件卸载时清理定时器
useEffect(() => {
return () => {
if (timerRef.current) {
clearTimeout(timerRef.current);
}
};
}, []);
return (
<img
ref={imageRef}
src={user?.image || "/images/person.jpg"}
alt="image"
className="w-8 h-8 rounded-full mt-[2px]"
onError={handleImageError}
/>
);
通过添加定时器,我们可以设定强制赋值备用图片地址的时间,在这里我设置的是2s可以根据各自情况修改。这样设置后,会给模板图片2s的加载时间,2s后打断。