react.js Replace img src onerror

2020-01-27 15:02发布

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;

16条回答
SAY GOODBYE
2楼-- · 2020-01-27 15:35

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;
查看更多
Viruses.
3楼-- · 2020-01-27 15:36

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

查看更多
Fickle 薄情
4楼-- · 2020-01-27 15:36

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

查看更多
唯我独甜
5楼-- · 2020-01-27 15:38

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';
        }
    }}
/>
查看更多
登录 后发表回答