Skip to content

Instantly share code, notes, and snippets.

@hartzis
Last active April 15, 2019 23:17
Show Gist options
  • Save hartzis/9b23d09f2c98019ddcf452f6916f09aa to your computer and use it in GitHub Desktop.
Save hartzis/9b23d09f2c98019ddcf452f6916f09aa to your computer and use it in GitHub Desktop.
react image component - utilizing recompose, handles errors
import React, { Component, PropTypes } from 'react';
import { setPropTypes, withState, lifecycle, mapProps, compose } from 'recompose';
export const ImageComponent = compose(
setPropTypes({ src: PropTypes.string.isRequired }),
withState('imgState', 'updateImgState', ({ src }) => ({ origSrc: src, currentSrc: src })),
lifecycle({
componentWillReceiveProps(nextProps) {
// if Image components src changes, make sure we update origSrc and currentSrc
if (nextProps.src !== nextProps.imgState.origSrc) {
nextProps.updateImgState({ origSrc: nextProps.src, currentSrc: nextProps.src });
}
},
}),
mapProps(({ imgState, updateImgState })=>{
return {
src: imgState.currentSrc,
// img load error: spread origSrc, and update new currentSrc to the fallback image
onError: () => updateImgState((s) => ({ ...s, currentSrc: 'FALLBACK_IMG_SRC' })),
};
})
)(Image);
class Image extends Component {
constructor() {
super();
this.assignImageRef = this.assignImageRef.bind(this);
}
componentDidMount() {
const { src } = this.props;
// if image hasn't completed loading, then let react handle error
if (!this._image.complete) return;
/*
* If an image has finished loading and has 'errored' (errored when naturalWidth === 0, or if svg check width)
* Then run the onError callback
* NOTE IF SVG: need to use width because firefox and IE assign naturalWidth from the svg elements width property,
* but some svgs don't have that specified
*/
const isInvalidSvg = srcRepresentsSvgFile(src) && !this._image.width;
const imgFailedToLoad = this._image.naturalWidth === 0;
if (isInvalidSvg || imgFailedToLoad) {
this.props.onError();
}
}
assignImageRef(r) {
this._image = r;
}
render() {
const { src, onError } = this.props;
return <img ref={ this.assignImageRef } src={ src } onError={ onError } />;
}
}
// checks for svg if last 4 characters === '.svg'
function srcRepresentsSvgFile(src) {
return src && /\.svg$/.test(src);
}
@hartzis
Copy link
Author

hartzis commented Jul 20, 2016

@hartzis
Copy link
Author

hartzis commented Sep 6, 2017

updated to handle svg

@hartzis
Copy link
Author

hartzis commented Oct 24, 2017

  • add regex for svg check
  • simplify check for error'ed images
  • use bound method for ref callback, avoids recreating anonymous function on every render

@bertho-zero
Copy link

bertho-zero commented Dec 22, 2017

withState('imgState', 'updateImgState', ({ src }) => { origSrc: src, currentSrc: src }),

should be (line 14)

withState('imgState', 'updateImgState', ({ src }) => ({ origSrc: src, currentSrc: src })),

@hartzis
Copy link
Author

hartzis commented Apr 11, 2019

@bertho-zero thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment