Image Pipeline with React Native ListView

Image Pipeline with React Native ListView

In mobile apps, scrolling through a list of images is a very common use case for which users have high expectations. In a previous story, I shared formulas that I used in order to cache images with RN. In this story, I would like to share how ListView can be used to optimize image processing when scrolling through images.

Consider a list of entries like an Instagram feed. We fetch a page from the server. At first, when the user scrolls towards the end of the list, we fetch more entries from the server. Secondly, when an entry enters the viewport, we trigger the image cache to display the image. Finally, when the entry goes outside the viewport, and if the image hasn’t been cached yet, we would like to cancel the request to the image URI in order to prioritize requests for the visible entries.

In the example below, we fetch entries from a firebase backend by pages of ten. When we reach the end of the list, we fetch a new page by using the onEndReached event.

export default class Feed extends Component<{}, void> {
    
    currentPage = 1;
    pageSize = 10;
    
    feed: ListViewDataSource =
        new ListView.DataSource({ rowHasChanged: (r1, r2) => r1.id !== r2.id });
    
    loadPage() {
        firebase.database().ref("/feed")
            .limitToFirst(this.pageSize * this.currentPage)
            .once("value").then(snapshot => {
                this.currentPage++;
                this.feed = this.feed.cloneWithRows(snapshot.val());
            });
    }
    
    componentWillMount() { this.loadFeed(); }

    render() {
        const {store} = this.props;
        return <ListView
            dataSource={store.feed}
            renderRow={row => <View><CachedImage source={{ uri: row.uri }} /></View> }
            onEndReached={() => this.loadFeed()}
        />;
    }
}

Now we can use onChangeVisibleRows() to only mount images when they are visible. We create a row component with a visible attribute. We use the ref() callback to keep a map up to date of each of the mounted rows. In onChangeVisibleRows(), we update the visible property for each row that becomes visible or invisible.

export default class Feed extends Component<{}, void> {

  //Keep a reference of all mounted Rows
  rows: { [id: string ]: any } = {};

  render() {
    return <ListView
      dataSource={store.feed}
      // We use ref to keep the reference of all mounted rows
      // The visible property will be set to true in onChangeVisibleRows()
      renderRow={row => <Row ref={el => this.rows[row.id] = el} visible={false} row={row} />}
      onEndReached={() => this.loadFeed()}
      onChangeVisibleRows={(visibleRows, changedRows) => {
        _.forEach(changedRows["s1"], (visible, index) => {
          // We get the reference of the mounted row
          //and set its property to true
          const id = this.feed.getRowData(0, index).id;
          this.rows[id].props.visible = visible;
        });
      }} />;
  }
}

In the Row component, we can use the visible property to mount/unmount the image according to its visibility. We use the react-native-img-cache package for this.

export default class Row extends Component<RowProps, void> {
  render() {
    const {visible, row} = this.props;
    return <View>
      {visible && <CachedImage source={{ uri: row.pictureURI }} />}
    </View>;
  }
}

In the last step, we cancel the HTTP request to download the image if the row becomes invisible. react-native-img-cache relies on react-native-fetch-blob which provides a way to both download image without sending it over the JS bridge and to cancel an HTTP request.

if(!visible) {
  ImageCache.get().cancel(row.uri);
}

Do you find this guide useful? I’m looking forward to reading your feedback.

要查看或添加评论,请登录

William Candillon的更多文章

  • React Native Gestures & Animations: you only have 16ms to render everything

    React Native Gestures & Animations: you only have 16ms to render everything

    The default Gesture and Animation APIs from React Native have some small declarative parts but they are mostly…

    1 条评论
  • "Can it be done in React Native?"?-?a case for declarative gestures and animations

    "Can it be done in React Native?"?-?a case for declarative gestures and animations

    Not long after starting React Native development, I started to look at the apps on my phone in a very different way…

  • Building a Product Live on YouTube

    Building a Product Live on YouTube

    Recently, I created a premium iOS and Android starter kit named Elements. The design is from Sketch Elements and the…

  • React Native Do with Firebase Integration

    React Native Do with Firebase Integration

    Since its release, the React Native Do starter kit has gained substantial traction and it was time to provide new…

  • Responsive UIs in React Native

    Responsive UIs in React Native

    Recently, I published a premium starter kit for RN named React Native DO. Like many React Native apps on the market…

  • The 80/20 of React Native

    The 80/20 of React Native

    When building a React Native app, what twenty percent of your effort brings eighty percent of the result? After…

  • Building a Fitness App with React Native

    Building a Fitness App with React Native

    I am an avid user of apps like Freeletics and Headspace which target the fitness of the body and mind. These apps have…

  • React Native Push Notifications with Firebase

    React Native Push Notifications with Firebase

    Last week, Swizec Teller wrote an fantastic tutorial on adding push notifications to your React Native app using Cloud…

    1 条评论
  • Writing Cloud Functions with Typescript

    Writing Cloud Functions with Typescript

    Serverless architectures are awesome and I recently started to use Cloud Functions (via Firebase). Immediately, I…

    1 条评论
  • Firebase Schema Evolution

    Firebase Schema Evolution

    Schema evolution is a natural part of your application lifecycle. Firebase is my go-to backend for web and mobile…

社区洞察

其他会员也浏览了