可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a react component that is the detail view from a list.
I am trying to replace the image with a default image if the image does not exist and there is a 404 error.
I would normally use the onerror method in the img tag but that doesn't seem to be working.
I am not sure how to do this with react.
Here is my component:
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;
回答1:
Since there is no perfect answer, I am posting the snippet I use. I am using reusable Image
component that fallbacks to fallbackSrc
.
Since the fallback image could fail again and trigger infinite loop of re-rendering, I added errored
state.
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,
};
回答2:
This works best for me
<img src={record.picture} onError={(e)=>{e.target.onerror = null; e.target.src="image_path_here"}}/>
回答3:
You can use uncontrolled component:
<img src={this.state.img} ref={img => this.img = img} onError={
() => this.img.src = 'img/default.img'
}>
回答4:
You need just define onError handler than change the state which will trigger component render method and eventually component will re-render with placeholder.
Please, don't use jQuery and React together!
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;
回答5:
Arthur's answer will result in infinite callbacks if fallback image also fails.
To avoid that, first set a state in the constructor for imageLoadError as true :
constructor(props) {
super(props);
this.state = {
imageLoadError: true,
};
}
and then check for this state value in onError
function to avoid infinite callbacks,
the code will look like this :-
<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:
Ran into a similar problem and the best solution i could find was Georgii Oleinikov's answer. (Doesn't require making new imageLoadError
state as suggested by Nitesh Ranjan in his answer)
onError={(e)=>{ if (e.target.src !== "image_path_here"){
e.target.onerror = null;
e.target.src="image_path_here";}
}
}
e.target.onerror = null
is not needed (and doesn't really help) because the if condition is enough to prevent the infinite loop(if backup image fails to load as well).
So:
onError={(e)=>{ if (e.target.src !== "image_path_here"){
e.target.src="image_path_here";}
}
}
EDIT: The other way around is to set a flag outside the return brackets and check for the flag in the if statement. Code should look something like this:
render(){
let errorflag=true;
return(
<img alt='' src={imageUrl}
onError={(e)=>{ if (errorflag){ errorflag=false; e.target.src=url; } }} />
);
}
回答7:
I took @Skay's answer and created a reusable Image component. Posting in case it helps anyone:
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:
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
}}
/>
this is what I'm currently using.
回答9:
@DepH's answer is nice, but it does produce and infinite loop if your error source also doesn't load. This helped me avoid the callback loop:
onError={(e)=>{ if (e.target.src !== "image_path_here")
{ e.target.onerror = null; e.target.src="image_path_here"; } }}
回答10:
For those like me who also wanted to change the styles of the element and/or change the img source, just do something like this:
<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
}}
/>
Hope it helps!
回答11:
Here's an answer using hooks:
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} />
}
And if you are want to handle the src
prop changing, you can pass a key
prop of the 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='...' />
The only extreme contrived edge case where using a key like this might fail is with sibling components. I think only one sibling node will render if they have the same key. To get around this you could probably wrap the Image in a <>
Fragment
.
<><Image key={srcProp} ... /></>
<><Image key={srcProp} ... /></>
回答12:
That's how I did it.
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}/>;
}
}
回答13:
You can use object
if that's ok with your requirement. Something like below will work perfectly fine
<object data={expected_image} type="image/jpg">
<img src={DEFAULT} alt="404" />
</object>
Check this answer for more details
https://stackoverflow.com/a/29111371/1334182
回答14:
I wrote like this.
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:
If anyone is using image src with require then onError doesn't work as -
<img src={require(`./../../assets/images/${props.imgName}.png`)} className="card-img" alt={props.name} />
then require throws an error, where I tried multiple ways and came to try and catch block solution as -
let imgSrc;
try {
imgSrc = require(`./../../assets/images/${props.imgName}.png`);
} catch {
imgSrc = require(`./../../assets/images/default.png`);
}
and use as
<img src={imgSrc} className="card-img" alt={props.name} />
回答16:
this works for me .
{<img className="images"
src={`/images/${student.src ? student.src : "noimage.png" }`} alt=
{student.firstname} />}
student is the name of my array and noimage the image, when there is no image is display.