Как условно отрендерить изображение в React? - PullRequest
6 голосов
/ 11 марта 2020

Представьте, что я хочу создать компонент для рендеринга изображения. Если ресурс изображения действителен, я должен отобразить его в div через css background-image. В противном случае я должен отобразить его как стандартный <img> с тегом alt.

Таким образом, мы получаем что-то вроде этого:

const ImageComponent = ({src}) => {
  const [loaded, setLoaded] = useState(false);
  const [renderFallback, setRenderFallback] = useState();

  useEffect(() => {
    const image = new Image();
    image.onsuccess = () => {
      setLoaded(true);
      setRenderFallback(false);
    }
    image.onerror = () => {
      setLoaded(true);
      setRenderFallback(true);
    }
    image.src = src;

    return () => {
      // clean up listeners
      image.onsuccess = undefined;
      image.onerror = undefined;
    }
  }, [src]);

  if (!loaded) { return null; }

  return renderFallback ?
    <div style={{backgroundImage: `url(${src})`}}/> :
    <img src={src} alt="my alt"/>;
}

Здесь мы сначала запускаем выборку для изображения с помощью обычный javascript (внутри useEffect). Если он действителен, ресурс кэшируется (браузером), и рендеринг последующего <div> происходит мгновенно.

Однако, если ресурс недействителен, то механизм кэширования отсутствует (см. этот вопрос для получения дополнительной информации). И поэтому, когда мы визуализируем новый элемент <img> в конце компонента, он запускает новый запрос на выборку этого изображения (несмотря на то, что он уже сломан).

Итак, чтобы избежать этого дубликата При извлечении, возможным решением было бы визуализировать уже инициализированный элемент image, а не новый реагирующий элемент для представления этого изображения.

Возможно ли это?


Обратная связь с комментариями

  • Недопустимым изображением является то, которое возвращает статус 404 , или указывает на ресурс, который на самом деле не является изображением (например, https://goodreads.com)
  • Я хотел бы отобразить «сломанное изображение» браузера по умолчанию в тех случаях, когда оно не работает , Это нельзя сделать с помощью background-image css, поэтому мне нужно полагаться на стандартный элемент <img>. Поскольку я уже определил, что изображение повреждено (в useEffect), я бы хотел избежать новой выборки при рендеринге поврежденного <img> в React.

Надеюсь, что это прояснит вещи!

1 Ответ

1 голос
/ 11 марта 2020

Мое определение допустимо: изображение загружено, и его ширина и высота больше нуля.

Теперь есть два способа проверить, является ли изображение действительным:

  • Предположим, что изображение недопустимо, попробуйте загрузить изображение, проверьте, является ли оно действительным, затем визуализируйте тег изображения.
  • Предположите, что изображение допустимо, отрендерируйте тег img, затем проверьте если изображение является действительным.

Следующий код сначала предполагает, что изображение недопустимо .

import React, { useEffect, useRef, useState } from "react";

const ImageComponent = ({src}) => {
    const container = useRef(null);
    const [valid, setValid] = useState(false);

    useEffect(() => {
        container.current.innerHTML = '';

        const image = new Image();

        const checkValid = () => {
            if (image.complete && image.naturalWidth > 0 && image.naturalHeight > 0) {
                setValid(true);
                container.current.appendChild(image);
            }
        }

        image.onload = checkValid;
        image.src = src;
    }, [src]);

    return (
        <div>
          <div ref={container} />
          {!valid && <div>Image not valid</div>}
        </div>
    );
};

Использование:

<ImageComponent src="IMAGE_URL" />

Следующий код сначала предполагает, что изображение является действительным .

import React, { useRef, useState } from 'react';

const ImageComponent = ({src}) => {
    const image = useRef(null);
    const [valid, setValid] = useState(true);

    const checkValid = () => {
        if (!image.current.complete || image.current.naturalWidth < 1 || image.current.naturalHeight < 1) setValid(false);
    }

    if (valid) {
        return (
            <img
                src={src}
                onLoad={checkValid}
                onError={() => setValid(false)}
                ref={image} 
            />
        );
    }

    return <div>Image not valid</div>;
};

Использование:

<ImageComponent src="IMAGE_URL" />
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...