lifechurch/melos

View on GitHub
src/components/images/LazyImage/index.js

Summary

Maintainability
A
3 hrs
Test Coverage
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Div, Img } from 'glamorous'
import WayPoint from 'react-waypoint'
import imgLoaded from '@youversion/utils/lib/images/imgLoaded'

class LazyImage extends Component {
  constructor(props) {
    super(props)
    this.state = {
      loaded: !!(this._img && imgLoaded(this._img)),
            // if we don't want lazy loading, we need to load immediately
      needsLoading: !props.lazy
    }
  }

  componentDidMount() {
    this.showImgIfNeeded()
  }

  componentDidUpdate() {
    this.showImgIfNeeded()
  }

  handleLoadImg = () => {
    const { loaded } = this.state
    if (!loaded) {
            // tell the img to actually load the src url
            // if we're lazy loading, this is false until the image enters the viewport
      this.setState({ needsLoading: true })
    }
  }

  showImgIfNeeded = () => {
    const { loaded } = this.state
        // we need to check this in case the img is already downloaded
        // and the onload doesn't fire
    if (!loaded && this._img && imgLoaded(this._img)) {
      this.setState({ loaded: true, needsLoading: false })
    }
  }

  render() {
    const {
            placeholder,
            src,
            width,
            height,
            className,
            imgClass,
            crossOrigin,
            lazy,
      borderRadius
        } = this.props
    const { loaded, needsLoading } = this.state

    const imgDiv = (
      <Img
        ref={(img) => {
          if (img) {
            // hopefully the src prop is unique...
            const imgSrc = img.props && img.props.src
            this._img = imgSrc
              && document.querySelector(`img[src="${imgSrc}"]`)
          }
        }}
        onLoad={() => { this.showImgIfNeeded() }}
        position={placeholder ? 'absolute' : 'static'}
        top={0}
        left={0}
        transition="opacity 0.5s linear"
        src={(needsLoading || loaded) ? src : null}
        width={width}
        height={height}
        crossOrigin={crossOrigin}
        opacity={loaded ? 1 : 0}
        borderRadius={borderRadius}
        className={`large ${loaded ? 'loaded' : ''} ${imgClass}`}
      />
        )

    let content = imgDiv
    if (placeholder) {
      content = (
        <Div
          className={className}
          backgroundColor={placeholder ? 'transparent' : 'lightGray'}
          backgroundSize="cover"
          backgroundRepeat="no-repeat"
          position="relative"
          overflow="hidden"
          width={width}
          height={height}
        >
          { placeholder }
          { imgDiv }
        </Div>
      )
    }

    let lazyimg = content
    if (lazy) {
      lazyimg = (
        <WayPoint onEnter={this.handleLoadImg} fireOnRapidScroll={false}>
          { content }
        </WayPoint>
      )
    }

    return lazyimg
  }
}

LazyImage.propTypes = {
  /**
   * Element (usually a default image) to show until
   * the src successfully downloads and covers it
   */
  placeholder: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
  /**
   * Determines if the src should be loaded instantly (false) or
   * only when in the viewport (true)
   */
  lazy: PropTypes.bool,
  /**
   * Image src
   */
  src: PropTypes.string,
  /**
   * Class to apply to wrapper div if a placeholder is passed
   */
  className: PropTypes.string,
  /**
   * Class to apply to image element
   */
  imgClass: PropTypes.string,
  /**
   * Width of wrapper if placeholder is passed, and image
   */
  width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  /**
   * Height of wrapper if placeholder is passed, and image
   */
  height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  /**
   * CORS setting for retrieving image from foreign origins
   */
  crossOrigin: PropTypes.string,
  /**
   * Border radius for image
   */
  borderRadius: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
}

LazyImage.defaultProps = {
  placeholder: null,
  lazy: true,
  src: '',
  className: '',
  imgClass: '',
  width: '100%',
  height: 'auto',
  crossOrigin: null,
  borderRadius: null,
}


export default LazyImage