替换 img src onerror

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

我试图替换的图像与默认图像,如果图像不存在,有一个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;
181343 次浏览

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

我接受了@Skay 的回答,创建了一个可重用的图片组件:

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;

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

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

我就是这么做的。

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

这样对我最好

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

这对我有用。

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

学生的名字是我的数组和没有图像的图像,当没有图像是显示。

对于那些像我一样想要改变元素的样式和/或改变 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
}}
/>

希望能有帮助!

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

这是我现在用的。

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

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

因为没有完美的答案,所以我将发布我使用的代码片段。我使用的是可重用的 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,
};

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

为了避免这种情况,首先在 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';
}
}}
/>

您可以使用 object,如果这与您的需求。像下面这样的东西将工作得非常好

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

查看这个答案以了解更多细节 Https://stackoverflow.com/a/29111371/1334182

遇到了类似的问题,我能找到的最好的解决方案是 Georgii Oleinikov的答案

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

这里有一个使用钩子的答案:

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道具的变化,你可以通过 src的一个 key道具。 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} ... /></>

我是这样写的。

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


如果任何人使用图像 src 与要求,那么 onError 不作为-

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

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

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

我使用了 Arthur 的上述方法,使 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 属性

以前的版本有这个缺陷; 它们没有考虑到 src可能被更改。所以我做出了我的终极解决方案:

  1. 支持输入
  2. 改变 src时的支持情况
  3. 前锋裁判
  4. 不忽略 onError(意味着你可以像通常对 <img />那样将 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}
/>
);
},
);

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

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

反应-图像-后备

正如在一篇评论中提到的,最好的解决方案是使用 反应形象库。使用 错误将失败,当您尝试服务静态版本的您的反应网站建立后。

下面是一个超级简单直观的例子,说明如何使用 反应形象,只需导入 Img 组件即可

import {Img} from 'react-image'

然后指定要加载的 src 列表

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

如果第一个网址没有找到,第二个将被加载,还有一些其他相当酷的功能,如显示一个微调器,而图像正在加载或显示一些其他组件的情况下,没有列出的图像可用

很简单

如果错误图像也加载失败 JSX

<img
src={props.image}
alt={props.title}
onError={(e) =>
(e.target.onerror = null)(
(e.target.src =
"https://upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Stadtbild_M%C3%BCnchen.jpg/2560px-Stadtbild_M%C3%BCnchen.jpg")
)
}
/>

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

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;

使用 React 函数组件、 Hook 和 TypeScript 更新2021答案

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




打字稿版本:

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

转发 Ref 和可能的 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} />
},
)

在上面@emil 的解决方案的帮助下 我创建了这个小功能组件。它在 First error 上使用一个回退 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='' />

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

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

2022年8月更新答案

el.target.onerror = null; //Not worked for me also not prevents looping.

最好的解决方案是将 errcount 属性设置为元素。

然后检查是否设置了属性,不需要再次设置 src。

也可以添加逻辑重试1,2,3... n 次。

<img onError={this.addDefaultSrc} src={BaseURL + item.ImageUrl} />


addDefaultSrc(el) {
var errcount = parseInt(el.target.getAttribute("errcount"));
if (isNaN(errcount)) {
el.target.setAttribute("errcount", "1");
el.target.src = BaseURL + 'Images/no-image.jpg';
}
//else
//el.target.src = "";
}