Как лучше всего иметь резервный образ в NextJS?
Недавно я работал над проектом в NextJS, который использует YoutubeAPI для получения информации о видео, включая URL-адреса миниатюр.
URL-адрес миниатюры для изображения с полным разрешением выглядит следующим образом:
Однако иногда YouTube не удается создать изображение с полным разрешением, и в этом случае изображение не отображается на моей веб-странице.
В случае, если изображение с URL
https://i.ytimg.com/vi/${videoId}/hqdefault.jpg
Как лучше всего справиться с этим с
7 ответов
Вы можете создать настраиваемый компонент изображения, который расширяет встроенный и добавляет логику отката, если изображение не загружается, запустив
onError
Перезвоните.
import React, { useState } from 'react';
import Image from 'next/image';
const ImageWithFallback = (props) => {
const { src, fallbackSrc, ...rest } = props;
const [imgSrc, setImgSrc] = useState(src);
return (
<Image
{...rest}
src={imgSrc}
onError={() => {
setImgSrc(fallbackSrc);
}}
/>
);
};
export default ImageWithFallback;
Затем вы можете напрямую использовать пользовательский компонент вместо
next/image
следующим образом:
<ImageWithFallback
layout="fill"
src={`https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg`}
fallbackSrc={`https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`}
/>
Эти ответы были полезны, но есть способ добиться этого без необходимости проходить
key
каждый раз, пользуясь
useEffect
крюк:
useEffect(() => {
set_imgSrc(src);
}, [src]);
Кроме того,
onError
событие, похоже, не срабатывает для определенных изображений (я полагаю,
layout='fill'
не запускает его в определенных сценариях), для тех случаев, когда я использовал
onLoadingComplete
а затем я проверяю, равна ли ширина изображения 0
onLoadingComplete={(result) => {
if (result.naturalWidth === 0) { // Broken image
set_imgSrc(fallbackSrc);
}
}}
Полный код:
import Image from "next/image";
import { useEffect, useState } from "react";
export default function ImageFallback({ src, fallbackSrc, ...rest }) {
const [imgSrc, set_imgSrc] = useState(src);
useEffect(() => {
set_imgSrc(src);
}, [src]);
return (
<Image
{...rest}
src={imgSrc}
onLoadingComplete={(result) => {
if (result.naturalWidth === 0) {
// Broken image
set_imgSrc(fallbackSrc);
}
}}
onError={() => {
set_imgSrc(fallbackSrc);
}}
/>
);
}
Есть решение @juliomalves с машинописным текстом
import React, { useState } from 'react';
import Image, { ImageProps } from 'next/image';
interface ImageWithFallbackProps extends ImageProps {
fallbackSrc: string
}
const ImageWithFallback = (props: ImageWithFallbackProps) => {
const { src, fallbackSrc, ...rest } = props;
const [imgSrc, setImgSrc] = useState(src);
return (
<Image
{...rest}
src={imgSrc}
onError={() => {
setImgSrc(fallbackSrc);
}}
/>
);
};
export default ImageWithFallback
@juliomalves дал 99% процентов ответа, однако я хотел бы добавить к нему. Возникла проблема при изменении src в его решении, поскольку изображение не обновлялось, потому что оно получало значение imgSrc, которое не обновляется. Это мое дополнение к его ответу:
import React, { useState } from 'react';
import Image from 'next/image';
const ImageFallback = (props) => {
const { src, fallbackSrc, ...rest } = props;
const [imgSrc, setImgSrc] = useState(false);
const [oldSrc, setOldSrc] = useState(src);
if (oldSrc!==src)
{
setImgSrc(false)
setOldSrc(src)
}
return (
<Image
{...rest}
src={imgSrc?fallbackSrc:src}
onError={() => {
setImgSrc(true);
}}
/>
);
};
export default ImageFallback;
Теперь imgSrc используется только как флаг, и есть отслеживание значения src, которое помогает изменить изображение, даже если у вас было изображение, на котором раньше было резервное изображение.
Ни одно из вышеперечисленных решений не сработало для меня. Но это сработало, когда я использовал «onErrorCapture» вместо «onError»! "="
<Image
loader={({ src }) => src}
src={imageUrl ? imageUrl : "/images/no-thumbnail.jpg"}
width={500}
height={500}
alt={imageUrl ? imageUrl : "/images/no-thumbnail.jpg"}
onError={(event) => {
event.target.id = "/images/no-thumbnail.jpg";
event.target.srcset = "/images/no-thumbnail.jpg";
}}
/>
onError вы можете установить srcset изображения. По изображению srcset nextjs image показывает изображения.
Это моя попытка создать<Avatar>
родительский компонент с и<Avatar.Fallback>
дочерние компоненты.
Во-первых, я построил<Avatar.Image>
компонент для отображения изображения нашего аватара.
import React, { useState } from 'react';
import Image from 'next/image';
function AvatarImage({ src, alt, className, ...props }) {
const [imageSuccessfullyLoaded, setImageSuccessfullyLoaded] = useState(true);
return (
<div>
{imageSuccessfullyLoaded && (
<Image
src={src}
alt={alt}
placeholder="empty"
onError={() => {
setImageSuccessfullyLoaded(false);
}}
fill
{...props}
/>
)}
</div>
);
}
export default AvatarImage;
ОднаждыAvatarImage
компонент не загружается, я позволяюAvatarFallback
компонент заполняет фон.
import React from 'react';
import classNames from 'classnames';
function AvatarFallback({ children, className }) {
return (
<div
className={classNames(
'text-lg font-medium text-neutral-600 dark:text-neutral-200',
className
)}
>
{children}
</div>
);
}
export default AvatarFallback;
Родительский компонент имеет имяAvatar
import React from 'react';
import classNames from 'classnames';
import AvatarImage from './AvatarImage/AvatarImage';
import AvatarFallback from './AvatarFallback/AvatarFallback';
function Avatar({ className, children }) {
return (
<div
className={classNames(
'relative flex items-center justify-center overflow-hidden rounded-full bg-neutral-200 dark:bg-neutral-600',
className
)}
>
{children}
</div>
);
}
Avatar.Image = AvatarImage;
Avatar.Fallback = AvatarFallback;
export default Avatar;
Ниже приведен пример того, как я его использую: (не забудьте изменить${videoId}
)
<Avatar>
<Avatar.Image src="https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg" alt="Avatar image" />
<Avatar.Fallback>
<Image
src="https://i.ytimg.com/vi/${videoId}/hqdefault.jpg"
fill
alt="Avatar image"
/>
</Avatar.Fallback>
</Avatar>