【问题标题】:How to conditionally render an image in React?如何在 React 中有条件地渲染图像?
【发布时间】:2020-06-23 07:19:12
【问题描述】:

假设我想创建一个组件来渲染图像。如果图像资源有效,我应该通过 css background-image 将其渲染为 div。否则,我应该将其呈现为带有 alt 标记的标准 <img>。

所以我们得到这样的结果:

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 中)开始获取图像。如果有效,则资源被缓存(由浏览器),随后的&lt;div&gt; 的呈现是瞬时的。

但是,如果资源无效,则没有适当的缓存机制(有关详细信息,请参阅this question)。因此,当我们在组件末尾渲染新的 &lt;img&gt; 元素时,它会启动一个全新的请求来获取该图像(尽管推断它已经损坏)。

因此,为了避免这种重复获取,一个可能的解决方案是渲染已经初始化的 image 元素,而不是一个新的 react 元素来表示该图像。

这可能吗?


来自 cmets 的反馈

  • 无效图像是指返回状态为 404 或指向的资源实际上不是图像(例如https://goodreads.com)
  • 我想在浏览器损坏的情况下显示浏览器的默认“损坏图像”。这不能通过background-image css 完成,所以我需要依赖一个标准的&lt;img&gt; 元素。由于我已经确定图像已损坏(在 useEffect 中),因此我想在 React 中渲染损坏的 &lt;img&gt; 时避免重新获取。

希望能澄清事情!

【问题讨论】:

  • 您好,您能解释一下您所说的无效图像吗?这是您要上传的图片吗?
  • 用户React.memo() 以避免不必要的重新渲染组件。更多详情请参考链接scotch.io/tutorials/…
  • 不确定您在这里尝试做什么,404 仍然需要时间。如果你说这个 404 总是无效的,你想以这种方式缓存它。然后你可以做的是使用 localeStorage 将此网址标记为无效..
  • @t3__rry 这与我链接的另一个问题有关。无效图像是指向非图像资源(例如https://goodreads.com)的图像,或者返回状态为 404 的图像
  • 我相信我理解你之后的内容,你的 React 代码看起来不远了,你没有传递 props 而是 src,所以这没有意义,但除此之外它看起来有点像修改后,您可以在使用 Session/LocaleStorage 后执行此操作,甚至可以选择自定义到期日期等。如果您可以敲出一个 sn-p,我可以更改它。

标签: javascript html css reactjs


【解决方案1】:

我对有效的定义是:图片已经加载完毕,并且宽度和高度都大于零。

现在有两种方法可以检查图像是否有效:

  • 假设图片无效,尝试加载图片,检查是否有效,然后渲染图片标签。
  • 假设图片有效,渲染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" />

【讨论】:

  • 嘿,谢谢或您的回答。情况实际上恰恰相反。如果图像有效,则显示 div。否则,&lt;img&gt;
  • 添加了替代代码,该代码将首先检查图像有效性然后呈现它。需要容器元素才能附加图像元素。
猜你喜欢
  • 2020-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多