react.js替换img src onerror

时间:2015-12-04 21:14:33

标签: javascript reactjs http-status-code-404

我有一个react组件,它是列表中的详细信息视图。

如果图像不存在且存在404错误,我试图用默认图像替换图像。

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

我不知道怎么做反应。

这是我的组件:

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;

27 个答案:

答案 0 :(得分:68)

这最适合我

<img src={record.picture} onError={(e)=>{e.target.onerror = null; e.target.src="image_path_here"}}/>

答案 1 :(得分:12)

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

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

答案 2 :(得分:8)

您只需定义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;

答案 3 :(得分:5)

2021 年使用 React 功能组件、钩子和 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} />
}


答案 4 :(得分:4)

遇到类似的问题,而我能找到的最佳解决方案是 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; } }} />
            );
} 

答案 5 :(得分:4)

如果后备图像也失败,亚瑟的答案将导致无限的回调。

为避免这种情况,请首先在构造函数中将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';
    }}
    }
/>

答案 6 :(得分:3)

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

import sqlite3
db=sqlite3.connect('E:/CLYDE/sqlite new2/dbase.db')
cursor=db.cursor()
cursor.execute('''CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT,
                       phone TEXT, email TEXT unique, password TEXT)''')
db.commit()

name1 = 'Andres'
phone1 = '3366858'
email1 = 'user@example.com'
# A very secure password
password1 = '12345'

name2 = 'John'
phone2 = '5557241'
email2 = 'johndoe@example.com'
password2 = 'abcdef'

users = [(name1,phone1, email1, password1),
         (name2,phone2, email2, password2)]
cursor.executemany(''' INSERT INTO users(name, phone, email, password) VALUES(?,?,?,?)''', users)
db.commit()

答案 7 :(得分:2)

我接受了@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;

答案 8 :(得分:2)

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

<ReactImageFallback
                    src="my-image.png"
                    fallbackImage="my-backup.png"
                    initialImage="loader.gif"
                    alt="cool image should be here"
                    className="my-image" />

react-image-fallback

答案 9 :(得分:2)

我使用上述Arthurs的方法使e.target.onerror = null停止了无限循环,但仍然发生了无限循环。因此,要停止无限循环,我必须使用以下方法。我必须找到实际的属性onError并将其设置为null。

<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

答案 10 :(得分:1)

由于没有完美的答案,因此我发布了我使用的代码段。我正在使用回退到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,
};

答案 11 :(得分:1)

import OriginalImage from '../../originalImg.png'
import ReplacementImage from '../../replaceImg.png'

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

这是我目前正在使用的。

答案 12 :(得分:1)

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

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

希望有帮助!

答案 13 :(得分:1)

它是如此简单

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

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

答案 14 :(得分:0)

借助上面@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='' />

答案 15 :(得分:0)

打字稿版本:

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} />
  },
)

答案 16 :(得分:0)

尝试使用此自定义图片组件:

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;

答案 17 :(得分:0)

我使用 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}
            />
        );
    }
}

答案 18 :(得分:0)

对于 SSR(服务器端渲染)...

所以,这里有一个(对我来说)有效的解决方法!

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 dep,第二个 hasRendered 确保第二次触发(或组件重新渲染)第一个钩子(在初始渲染之后),然后强制设置图像 src在那个钩子中,然后触发客户端上的事件!

答案 19 :(得分:0)

正如其中一条评论中提到的那样,最好的解决方案是使用react-image库。在构建后尝试提供静态版本的React网站时,使用 onError 将会失败。

这是一个超级简单明了的示例,该示例如何使用反应图像,只需导入Img组件

import {Img} from 'react-image'

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

<Img
   src={['images/image1.svg', 'images/default.svg']}
   alt="Some title"
/>   

如果找不到第一个网址,则将加载第二个网址,还有其他一些很酷的功能,例如在加载图片时显示微调框,或在没有列出的图片可用的情况下显示其他组件

答案 20 :(得分:0)

以前的版本存在错误;他们不认为src可以更改。因此,我制定了最终的解决方案:

  1. 支持打字
  2. 更改src时的支持情况
  3. 转发参考
  4. 不要忽略onError(意味着您可以像平时对onError一样将ImageWithFallback传递给<img />

这里是:

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}
      />
    );
  },
);

答案 21 :(得分:0)

如果有人将图像src与require一起使用,则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} />

答案 22 :(得分:0)

我这样写。

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);

答案 23 :(得分:0)

以下是使用钩子的答案:

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属性的更改,则可以传递key中的src属性。 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='...' />

使用这种键可能失败的唯一极端人为边缘情况是兄弟组件。我认为,只有一个同级节点具有相同的密钥才能渲染。为了解决这个问题,您可以将图像包装在<> Fragment中。

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

答案 24 :(得分:0)

如果可以的话,您可以使用object。像下面这样的东西会很好用

<object data={expected_image} type="image/jpg">
  <img src={DEFAULT} alt="404" />
</object>

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

答案 25 :(得分:0)

我是怎么做到的。

 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}/>;
          } 
    }

答案 26 :(得分:-1)

这对我有用。

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

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