react.js 替换 img src onerror

Posted

技术标签:

【中文标题】react.js 替换 img src onerror【英文标题】:react.js Replace img src onerror 【发布时间】:2016-03-09 22:19:24 【问题描述】:

我有一个反应组件,它是列表中的详细视图。

如果图像不存在并且出现 404 错误,我正在尝试将图像替换为默认图像。

我通常会在 img 标签中使用 onerror 方法,但这似乎不起作用。

我不确定如何使用 react 来做到这一点。

这是我的组件:

import React from 'react';
import Link from 'react-router';
import ContactStore from '../stores/ContactStore'
import ContactActions from '../actions/ContactActions';

class Contact extends React.Component 
  constructor(props) 
    super(props);
    this.state = ContactStore.getState();
    this.onChange = this.onChange.bind(this); 
 

componentDidMount() 
  ContactStore.listen(this.onChange);
  ContactActions.getContact(this.props.params.id);


componentWillUnmount() 
  ContactStore.unlisten(this.onChange);


componentDidUpdate(prevProps) 
  if (prevProps.params.id !== this.props.params.id) 
    ContactActions.getContact(this.props.params.id);
  


onChange(state) 
  this.setState(state);


render() 
  return (
    <div className='container'>
      <div className='list-group'>
        <div className='list-group-item animated fadeIn'>
          <h4>this.state.contact.displayname</h4>
          <img src=this.state.imageUrl />
        </div>
      </div>
    </div>
  );



export default Contact;

【问题讨论】:

我遇到了这个问题,目前我没有代码可以提供帮助,但我所做的是将 javascript 检查放在 componentdidmount 中,它会查找图像错误,如果它们发生, 会触发一个回调,用默认图像替换该图像。 请接受 Georgii Oleinikov 的答案,因为目前得分最高的答案可能会产生永恒的循环,所以一点也不好。 找到了这个视频教程 - youtu.be/90P1_xCaim4 它实际上帮助我为我的应用程序构建了一个完整的图像组件。我还发现了这个以及我的图像组件的一个很棒的预加载器 - youtu.be/GBHBjv6xfY4。通过将两者结合起来,您可以为用户提供出色的用户体验。 【参考方案1】:

这对我最有效

<img 
  src=record.picture
  onError=( currentTarget ) => 
    currentTarget.onerror = null; // prevents looping
    currentTarget.src="image_path_here";
  
/>

【讨论】:

如果“image_path_here”产生并出错,这个方法似乎会导致无限回调...... @tomhughes 它会在“image_path_here”失败时防止无限回调 @DeepakMallah 我遵循了您的代码&lt;img src=imageUrl className=cs.image onError=(e) =&gt; e.target.src = 'https://upload.wikimedia.org/wikipedia/en/c/c3/The_Martian_2014.jpg'; e.target.onError = null; /&gt; 但是,Safari 中的控制台仍然显示错误Failed to load resource: the server responded with a status of 404 (Not Found)。正常吗?如何删除此控制台错误?谢谢。 感谢您的回答。真的很有帮助。 @vizsatiz 尝试用e.currentTarget替换e.target【参考方案2】:

对于 s-s-r(服务器端渲染)...

所以,这是一个可行的解决方法(对我来说)!

const Img: FC<
  DetailedhtmlProps<ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>
> = ( src, ...props ): JSX.Element => 
  const [hasRendered, setHasRendered] = useState(false);
  const imgRef = useRef<HTMLImageElement | null>(null);

  useEffect(() => 
    if (imgRef.current && hasRendered) 
      imgRef.current!.src = src || '';
    
  , [src, hasRendered]);

  useEffect(() => 
    setHasRendered(true);
  , []);

  return (
    <img
      ...props
      ref=imgRef as any
      alt=props.alt || 'image'
      aria-hidden=true
      onError=...
      onLoad=...
    />
  );
;

所以,魔法发生在两个useEffect 挂钩中。 (仅使用一个不起作用)。 基本上,第二个useEffect 确保第二次触发(或组件重新渲染)第一个钩子(在初始渲染之后),由于hasRendered dep,然后强制图像 src 设置在该钩子中然后触发客户端上的事件!

【讨论】:

【参考方案3】:

我使用 TypeScript 扩展了 @Emils 解决方案并添加了

加载时支持占位符
import * as React from "react";

type Props = 
    src: string,
    fallbackSrc: string,
    placeholderColor?: string,
    className?: string,


type State = 
    src: string,
    errored: boolean,
    loaded: boolean


export default class Image extends React.Component<Props, State> 
    constructor(props: Props) 
        super(props);

        this.state = 
            src: props.src,
            errored: false,
            loaded: false
        ;
    

    onError = () => 
        if (!this.state.errored) 
            this.setState(
                src: this.props.fallbackSrc,
                errored: true,
            );
        
    

    onLoad = () => 
        if(!this.state.loaded)
            this.setState(loaded: true);
        
    

    render() 
        let style = 
            backgroundColor: this.props?.placeholderColor || "white"
        ;

        if(this.state.loaded)
            style.backgroundColor = "transparent";
        

        return (
            <img
                style=style
                onLoad=this.onLoad
                onError=this.onError
                ...this.props
                src=this.state.src
            />
        );
    

【讨论】:

【参考方案4】:

2021 年使用 React 功能组件、Hooks 和 TypeScript 更新答案

// ImageWithFallback.tsx
import React,  ImgHTMLAttributes, useState  from 'react'

interface Props extends ImgHTMLAttributes<any> 
  fallback: string


export default function ImageWithFallback( fallback, src, ...props : Props) 
  const [imgSrc, setImgSrc] = useState<string | undefined>(src)
  const onError = () => setImgSrc(fallback)

  return <img src=imgSrc ? imgSrc : fallback onError=onError ...props />



【讨论】:

你不需要跟踪错误,只在状态改变时重新渲染。将值从fallback 设置为fallback 不会导致重新渲染,const onError = () =&gt; setImgSrc(fallback); 很好..【参考方案5】:

借助上面@emil 的解决方案 我创建了这个小功能组件。它在第一个错误时使用后备 src,并在第二个错误时从后备 src 中删除 img。

import React,  useState  from 'react'

function ImageFallback( src, fallbackSrc, ...props ) 

    const [state, setState] = useState( src: src, errored: false )
   

    //update(next img) state onMount 
    useEffect(() => 
       setState(
           src: src,
           errored: false,
       )

    , [src])

   //update (remove) state onUnMount
   useEffect(() => 
       return () => 
           setState(
               src: null,
               errored: false,
           )
       
   , [])

    const onError = () => 
        //1st error
        if (!state.errored) 
            setState(
                src: fallbackSrc,
                errored: true,
            );
         else if (state.errored && state.src) 
            //2nd error
            //when error on fallbacksrc - remove src
            setState(
                src: null,
                errored: true,
            );
        

    

    return (
        state.src && <img
            src=state.src
            onError=onError
            ...props
        />
    )


export default ImageFallback

用法...

 <ImageFallback src=anySrc fallbackSrc=anyFallbackSrc className=classes.logo alt='' />

【讨论】:

【参考方案6】:

打字稿版本:

const Avatar = (): JSX.Element => 
    function imageErrorHandler(e: React.SyntheticEvent<HTMLImageElement, Event>) 
      const el = e.target as HTMLImageElement
      el.onerror = null
      el.src = '/fallback.png'
    

    return <img src='/smth.png' onError=imageErrorHandler/>
  ,
)

使用 forwardRef 和可能的 null src:

import  forwardRef  from 'react'

type Props = Omit<React.ComponentPropsWithoutRef<'img'>, 'src'> &  src?: null | string 

const Avatar = forwardRef<HTMLImageElement, Props>(
  ( src, ...rest , ref): JSX.Element => 
    function imageErrorHandler(e: React.SyntheticEvent<HTMLImageElement, Event>) 
      const el = e.target as HTMLImageElement
      el.onerror = null
      el.src = '/fallback.png'
    

    return <img src=src || '/alternative.png' onError=imageErrorHandler ref=ref ...rest />
  ,
)

【讨论】:

【参考方案7】:

试试这个自定义图像组件:

import React,  useRef  from 'react';
import PropTypes from 'prop-types';

import defaultErrorImage from 'assets/images/default-placeholder-image.png';

const Image = ( src, alt, className, onErrorImage ) => 
  const imageEl = useRef(null);
  return (
    <img
      src=src
      alt=alt
      className=className
      onError=() => 
        imageEl.current.src = onErrorImage;
      
      ref=imageEl
    />
  );
;

Image.defaultProps = 
  onErrorImage: defaultErrorImage,
;

Image.propTypes = 
  src: PropTypes.string.isRequired,
  alt: PropTypes.string.isRequired,
  className: PropTypes.string.isRequired,
  onErrorImage: PropTypes.string,
;

export default Image;

【讨论】:

【参考方案8】:

就这么简单

e.target.onerror = null 如果错误图片也无法加载 jsx

<img 
   src=imageSrc
   onError=(e) => (e.target.onerror = null, e.target.src = imageErrorSrc)/>

【讨论】:

如果imageErrorSrc 是一个无效的url,那么onError 会无限运行。为什么?【参考方案9】:

正如其中一个 cmets 所述,最好的解决方案是使用 react-image 库。当您尝试在构建后提供静态版本的 react 网站时,使用 onError 将失败。

这里有一个超级简单直接的例子如何使用react-image,只需要导入Img组件

import Img from 'react-image'

然后指定您尝试加载的 src 列表

<Img
   src=['images/image1.svg', 'images/default.svg']
   
/>   

如果未找到第一个 url,则将加载第二个,还有一些其他非常酷的功能,例如在加载图像时显示微调器或在没有列出的图像可用的情况下显示其他组件

【讨论】:

【参考方案10】:

即使这是一个老问题,如果您正在寻找一个干净的解决方案,您也可以使用 react-image-fallback 库。

<ReactImageFallback
                    src="my-image.png"
                    fallbackImage="my-backup.png"
                    initialImage="loader.gif"
                    
                    className="my-image" />

react-image-fallback

【讨论】:

最后一次发布对这个 repo 的更改是在两年前。另一种方法是react-image。 使用 react-image 应该是被接受的答案,因为它确实解决了您在为 react 网站提供静态构建时可能面临的所有问题 我使用npmjs.com/package/react-image 实现了目标。您的回答启发了我寻找 npm 而不是创建自己的解决方案。非常感谢。 :)【参考方案11】:

以前的版本有这个bug;他们不认为src 可以更改。所以我制定了我的最终解决方案:

    支持打字 src 更改时的支持案例 转发参考 不忽略onError(意味着您可以像通常使用&lt;img /&gt; 一样将onError 传递给ImageWithFallback

这里是:

import React,  useState, useCallback, useEffect  from 'react';
import noImage from 'src/svg/no-image.svg';

export const ImageWithFallback = React.forwardRef(
  (
    
      onError,
      ...props
    : React.DetailedHTMLProps<
      React.ImgHTMLAttributes<HTMLImageElement>,
      HTMLImageElement
    >,
    ref: React.Ref<HTMLImageElement>,
  ) => 
    const [imageLoadFailed, setImageLoadFailed] = useState<boolean>(false);

    const handleError = useCallback(
      (e: React.SyntheticEvent<HTMLImageElement, Event>) => 
        if (imageLoadFailed) return;
        setImageLoadFailed(true); // to avoid infinite loop
        if (onError) 
          onError(e);
        
      ,
      [imageLoadFailed, setImageLoadFailed, onError],
    );

    useEffect(() => 
      setImageLoadFailed(false); // in case `src` is changed
    , [props.src]);

    return (
      <img
        ...props
        src=imageLoadFailed ? noImage : props.src
        onError=handleError
        ref=ref
      />
    );
  ,
);

【讨论】:

【参考方案12】:

我用上面的 Arthurs 制作 e.target.onerror = null 的方法来停止无限循环,但仍然发生了无限循环。所以,要停止无限循环,我必须使用下面的方法。我必须找到实际的属性 onError 并使其为空。

<img src=imageSource
     onError=(e) =>  
              e.target[Object.keys(e.target).filter(prop=>prop.includes('EventHandler'))[0]].onError = null;
              e.target.src = 'images/avatar.png'; 
 />

event.target properties

【讨论】:

【参考方案13】:

如果有人使用带有 require 的图像 src,那么 onError 就不起作用 -

<img src=require(`./../../assets/images/$props.imgName.png`) className="card-img" alt=props.name />

然后 require 抛出一个错误,我尝试了多种方法并尝试捕获块解决方案 -

  let imgSrc;
  try 
    imgSrc = require(`./../../assets/images/$props.imgName.png`);  
   catch 
    imgSrc = require(`./../../assets/images/default.png`);
  

并用作

<img src=imgSrc className="card-img" alt=props.name />

【讨论】:

【参考方案14】:

我是这样写的。

import React,  useState  from 'react';
import NoImageSVG from './noImage.svg';

const ImgWithFallback: React.FunctionComponent< src: string; alt: string; className: string > = (
  src,
  alt,
  className,
) => 
  const [isUndefined, updateIsUndefined] = useState(false);

  const onError = () => 
    updateIsUndefined(true);
  ;

  if (isUndefined) 
    return (
      <div className=className>
        <NoImageSVG width='5rem' height='5rem' />
      </div>
    );
  

  return <img src=src alt=alt className=className onError=onError />;
;

export default React.memo(ImgWithFallback, () => true);

【讨论】:

【参考方案15】:

如果后备图片也失败,Arthur 的回答将导致无限回调。

为避免这种情况,首先在构造函数中将 imageLoadError 的状态设置为 true :

constructor(props) 
    super(props);
    this.state = 
      imageLoadError: true,
    ;

然后在onError函数中检查这个状态值以避免无限回调,

代码将如下所示:-

<img
    src="https://if_this_url_fails_go_to_onError"
    onError=e =>  
        if(this.state.imageLoadError)  
            this.setState(
                imageLoadError: false
            );
            e.target.src = 'fallbackImage.png';
        
    
/>

【讨论】:

【参考方案16】:

这是一个使用钩子的答案:

import React,  useState  from 'react'

/**
 * Returns an object that can 
 * be spread onto an img tag
 * @param String img
 * @param String fallback
 * @returns Object  src: String, onError: Func 
*/
function useFallbackImg(img, fallback) 
  const [src, setImg] = useState(img)

  function onError(e) 
    console.log('Missing img', img, e)
    // React bails out of hook renders if the state
    // is the same as the previous state, otherwise
    // fallback erroring out would cause an infinite loop
    setImg(fallback)
  

  return  src, onError 


/**
 * Usage <Image src='someUrl' fallback='fallbackUrl' alt='something' />
 */
function Image(src, fallback, ...rest) 

  const imgProps = useFallbackImg(src, fallback)

  return <img ...imgProps ...rest />

如果你想处理src 属性的变化,你可以传递srckey 属性。 https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key

<Image key='someUrl' src='someUrl' fallback='fallbackUrl' alt='...' />

使用这样的键可能会失败的唯一极端人为的边缘情况是使用同级组件。我认为如果它们具有相同的密钥,则只有一个兄弟节点会呈现。为了解决这个问题,您可以将图像包装在 &lt;&gt; Fragment 中。

<><Image key=srcProp ... /></>
<><Image key=srcProp ... /></>

【讨论】:

【参考方案17】:

遇到了类似的问题,我能找到的最佳解决方案是 Georgii Oleinikov 的回答。 (不需要按照 Nitesh Ranjan 在他的回答中的建议创建新的 imageLoadError 状态)

onError=(e)=> if (e.target.src !== "image_path_here")
                    e.target.onerror = null;
                     e.target.src="image_path_here";
                
           

e.target.onerror = null 不是必需的(并且没有真正帮助),因为 if 条件足以防止无限循环(如果备份图像也无法加载)。

所以:

onError=(e)=> if (e.target.src !== "image_path_here")
                 e.target.src="image_path_here";
               
         

编辑:另一种方法是在返回括号外设置一个标志并检查 if 语句中的标志。代码应如下所示:

render()
 let errorflag=true;
 return(
            <img alt='' src=imageUrl 
                    onError=(e)=> if (errorflag) errorflag=false; e.target.src=url;   />
            );
 

【讨论】:

【参考方案18】:

如果您的要求没问题,您可以使用object。像下面这样的东西会很好用

<object data=expected_image type="image/jpg">
  <img src=DEFAULT  />
</object>

查看此答案以获取更多详细信息 https://***.com/a/29111371/1334182

【讨论】:

【参考方案19】:

由于没有完美的答案,我发布了我使用的 sn-p。我正在使用可重用的 Image 组件,该组件回退到 fallbackSrc

由于后备图像可能再次失败并触发重新渲染的无限循环,我添加了errored状态。

import React,  Component  from 'react';
import PropTypes from 'prop-types';

class Image extends Component 
  constructor(props) 
    super(props);

    this.state = 
      src: props.src,
      errored: false,
    ;
  

  onError = () => 
    if (!this.state.errored) 
      this.setState(
        src: this.props.fallbackSrc,
        errored: true,
      );
    
  

  render() 
    const  src  = this.state;
    const 
      src: _1,
      fallbackSrc: _2,
      ...props
     = this.props;

    return (
      <img
        src=src
        onError=this.onError
        ...props
      />
    );
  


Image.propTypes = 
  src: PropTypes.string,
  fallbackSrc: PropTypes.string,
;

【讨论】:

一点注意事项:如果您使用 React 和服务器端渲染,则无法正常工作,因为图像是异步加载的,并且在进行水合时,所有错误都已被触发。 我使用 TypeScript 扩展了这个答案并添加了占位符支持 ***.com/a/68378797/3457769【参考方案20】:

@DepH 的回答很好,但如果您的错误源也没有加载,它确实会产生无限循环。这帮助我避免了回调循环:

onError=(e)=> if (e.target.src !== "image_path_here") 
     e.target.onerror = null; e.target.src="image_path_here";  

【讨论】:

e.target.onerror = null 是不需要的。不过,这必须被接受。【参考方案21】:
import OriginalImage from '../../originalImg.png'
import ReplacementImage from '../../replaceImg.png'

<img
 src= OriginalImage
 
 onError=(e) => 
    e.target.src = ReplacementImage //replacement image imported above
    e.target.style = 'padding: 8px; margin: 16px' // inline styles in html format
 
/>

这是我目前正在使用的。

【讨论】:

【参考方案22】:

对于像我这样也想更改元素样式和/或更改 img 源的人,只需执行以下操作:

<img
  src='original src url goes here'
  
  onError=(e) => 
     e.target.src = '/example/noimage.png' // some replacement image
     e.target.style = 'padding: 8px; margin: 16px' // inline styles in html format
  
/>

希望对你有帮助!

【讨论】:

【参考方案23】:

这对我有用。

<img className="images"
    src=`/images/$student.src ? student.src : "noimage.png" ` alt=  
student.firstname /> 

student 是我的数组的名称,noimage 是图像,当没有图像时显示。

【讨论】:

【参考方案24】:

我就是这样做的。

 class Pix extends React.Component

          constructor(props)
            super(props);
           this.state=link: this.props.link;
           this.onError=this.onError.bind(this);
          


          onError()
              console.log("error: could not find picture");
              this.setState(function() return link: "missing.png"; );
             ;

          render()
          return <img onError=this.onError src=this.state.link/>;
           
    

【讨论】:

【参考方案25】:

你可以使用不受控制的组件:

<img src=this.state.img ref=img => this.img = img onError=
    () => this.img.src = 'img/default.img'
>

【讨论】:

【参考方案26】:

我接受了@Skay 的回答并创建了一个可重用的 Image 组件。发帖以防万一:

import React,  PropTypes  from 'react';

const Image = (src, fallbackSrc, ...other) => 
    let element;
    const changeSrc = newSrc => 
        element.src = newSrc;
    ;
    return (
        <img src=src 
             onError=() => changeSrc(fallbackSrc) 
             ref=el => element=el 
             ...other />
    );
;

Image.propTypes = 
    src: PropTypes.string,
    fallbackSrc: PropTypes.string
;
export default Image;

【讨论】:

不应将状态存储在&lt;img /&gt; 组件中,而应将其存储在包含&lt;Image /&gt; 的组件中,这意味着将其重构为有状态组件并使用this.setState( src: defaultSrc ) 之类的东西。跨度> 【参考方案27】:

您只需要定义 onError 处理程序,而不是更改将触发组件渲染方法的状态,最终组件将使用占位符重新渲染。

请不要同时使用 jQuery 和 React!

import React from 'react';
import Link from 'react-router';
import ContactStore from '../stores/ContactStore'
import ContactActions from '../actions/ContactActions';

class Contact extends React.Component 
  constructor(props) 
    super(props);
    this.state = ContactStore.getState();
    this.onChange = this.onChange.bind(this); 
 

componentDidMount() 
  ContactStore.listen(this.onChange);
  ContactActions.getContact(this.props.params.id);


componentWillUnmount() 
  ContactStore.unlisten(this.onChange);


componentDidUpdate(prevProps) 
  if (prevProps.params.id !== this.props.params.id) 
    ContactActions.getContact(this.props.params.id);
  


onChange(state) 
  this.setState(state);


onError() 
  this.setState(
    imageUrl: "img/default.png"
  )


render() 
  return (
    <div className='container'>
      <div className='list-group'>
        <div className='list-group-item animated fadeIn'>
          <h4>this.state.contact.displayname</h4>
          <img onError=this.onError.bind(this) src=this.state.imageUrl />
        </div>
      </div>
    </div>
  );


export default Contact;

【讨论】:

但是 bootstrap 需要 Jquery 兄弟 @GuilhermeNunes React Bootstrap 没有!

以上是关于react.js 替换 img src onerror的主要内容,如果未能解决你的问题,请参考以下文章

动态导入图像(React Js)(需要 img 路径找不到模块)

用js的正则把img src替换成img data-original把img标签中的src属性名称替换成data-original

jquery 获取img 的src的值然后替换指定img的src怎么写??急!!!

正则把<img src="**">替换成<img data-original="**">把img标签中的src属性名称替换成data-o

PHP - 替换 <img> 标签并返回 src

jQuery:基于src收集img标记,然后替换它们