Lazy image loading

Sometimes, you don't necessarily want an image to load at the exact moment that it's rendered. For example, you might be rendering something that's not yet visible on the screen. Most of the time, it's perfectly fine to fetch the image source from the network before it's actually visible. But if you're fine-tuning your application and discovering that loading lots of images over the network causes performance issues, you can lazily load the source.

I think the more common case in a mobile context is handling a scenario where you've rendered one or more images where they're visible, but the network is slow to respond. In this case, you will probably want to render a placeholder image so that the user sees something right away, rather than empty space.

To do this, we'll implement an abstraction that wraps the actual image that we want to show once it's loaded. Here's the code:

import React, { Component, PropTypes } from 'react'; 
import { View, Image } from 'react-native'; 
 
// The local placeholder image source. 
const placeholder = require('./images/placeholder.png'); 
 
// The mapping to the "loaded" state that gets us 
// the appropriate image component. 
const Placeholder = props => 
  new Map([ 
    [true, null], 
    [false, ( 
      <Image 
        {...props} 
        source={placeholder} 
      /> 
    )], 
  ]).get(props.loaded); 
 
class LazyImage extends Component { 
  // The "width" and "height" properties 
  // are required. All other properties are 
  // forwarded to the actual "<Image>" 
  // component. 
  static propTypes = { 
    style: PropTypes.shape({ 
      width: PropTypes.number.isRequired, 
      height: PropTypes.number.isRequired, 
    }), 
  }; 
 
  constructor() { 
    super(); 
 
    // We assume that the source hasn't finished 
    // loading yet. 
    this.state = { 
      loaded: false, 
    }; 
  } 
 
  render() { 
    // The props and state this component 
    // needs in order to render... 
    const { 
      props: { 
        style: { 
          width, 
          height, 
        }, 
      }, 
      state: { 
        loaded, 
      }, 
    } = this; 
 
    return ( 
      <View style={{ width, height }}> 
        { /* The placeholder image is just a standard 
             "<Image>" component with a predefined 
             source. It isn't rendered if "loaded" is 
             true. */ } 
        <Placeholder loaded={loaded} {...this.props} /> 
        { /* The actual image is forwarded props that 
             are passed to "<LazyImage>". The "onLoad" 
             handler ensures the "loaded" state is true, 
             removing the placeholder image. */ } 
        <Image 
          {...this.props} 
          onLoad={() => this.setState({ 
            loaded: true, 
          })} 
        /> 
      </View> 
    ); 
  } 
} 
 
export default LazyImage; 

This component renders a View with two Image components inside. It also has a loaded state, which is initially false. When loaded is false, the placeholder image is rendered. The loaded state is set to true when the onLoad() handler is called. This means that the placeholder image is removed, and the main image is displayed.

Now let's use the LazyImage component that we've just implemented. We'll render the image without a source, and the placeholder image should be displayed. We'll add a button that gives the lazy image a source, and when it loads, the placeholder image should be replaced. Here's what the main app module looks like:

/* eslint-disable global-require */ 
import React, { Component } from 'react'; 
import { 
  AppRegistry, 
  View, 
} from 'react-native'; 
 
import styles from './styles'; 
import LazyImage from './LazyImage'; 
import Button from './Button'; 
 
// The remote image to load... 
const remote = 'https://facebook.github.io/react/img/logo_small_2x.png'; 
 
class LazyLoading extends Component { 
  state = { 
    source: null, 
  } 
 
  render() { 
    return ( 
      <View style={styles.container}> 
        { /* Renders the lazy image. Since there's 
             no "source" value initially, the placeholder 
             image will be rendered. */ } 
        <LazyImage 
          style={{ width: 200, height: 100 }} 
          resizeMode="contain" 
          source={this.state.source} 
        /> 
        { /* When pressed, this button changes the 
             "source" of the lazy image. When the new 
             source loads, the placeholder image is 
             replaced. */ } 
        <Button 
          label="Load Remote" 
          onPress={() => this.setState({ 
            source: { uri: remote }, 
          })} 
        /> 
      </View> 
    ); 
  } 
} 
 
AppRegistry.registerComponent( 
  'LazyLoading', 
  () => LazyLoading 
); 

This is what the screen looks like initially:

Lazy image loading

Then, if you click the Load Remote button, you'll eventually see the image that we actually want:

Lazy image loading

You might notice that depending on your network speed, the placeholder image remains visible even after you click the Load Remote button. This is by design, because you don't want to remove the placeholder image until you know for sure that the actual image is ready to be displayed.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.183.89