3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

React-nativeでInfinite Scrollのページネーションを実装する

3
Last updated at Posted at 2021-01-11

目的・環境

  • Firebase→React-native→FlatListの構成でページネーションを実装する
  • 読み込み方法はInfinite Scrollを行う
  • 既存の日本語の記事では、Reduxを交えた高度な構成であったり、React-nativeのFlatListを利用した事例がなかったり、何より、最小限のシンプルな例が見当たらなかったため、整理することにした

ゴール

  • Firebaseでページネーションを行う
  • React-natigve、アプリでスクロールで更新する
  • FlatList を使う <ーこれを重視しました。よく使うリストの作り方だと思うので
  • 最小限の部分を把握する

実装

概要

  • Firebaseに、createtAt でタイムスタンプを設定している。そのタイムスタンプ降順でソートする
    今回はタイムスタンプだが、idでもなんでもソートできればいい
  • 一度の読み込みサイズは2レコード
  • ページ下部到達で、追加の2レコードを表示する
  • 読み込み中はインジケータを表示する

コードと解説

  • ポイント
  • stateに最後に読んだデータを記録しておく ->lastVisible
  • lastVisibleは、orderbyでソート軸にした項目
  • クエリの条件で .startAfter(lastVisible) を追加しカーソルを設定
    言うまでもなく .limit(バッチサイズ) で絞る
  • FlatListに属性を追加する onEndReached={this.fetchMore} onEndReachedThreshold={0}
  • リストが最下部にきたら onEndReached で指定したメソッドが実行される。そこで再読み込み処理を書く
app.js

class サンプルクラス extends React.Component {
  state = {
    items: [],
    refreshing: false,
    limit: 2, // 今回はここに読み込むバッチサイズを定義しておく。
    lastVisible: null,
    loading: false,
  }

constructor(props) {
    super(props);
    this.listLoading(props); // 初回ロードを実行

  }
  // 初回ロード リストを遅延ローディングする
 // ここは単純に画面表示直後のデータを取り込んでいるだけ
  // チェックするポイントは以下
  listLoading = async (props) => {
    // 読み込み中を開始する
    this.setState({
        loading: true,
      });
   const firestore = firebase.firestore();
   await firestore.collection"コレクション")
     // 今回は、ソート順を、作成時刻降順、としているため、タイムスタンプを利用
     .orderBy("createdAt", "desc") 
     .limit(limit).then((items) => {
        // 取得結果の最後をstateに保持する
        let lastVisible = items[items.length - 1].createdAt;
        this.setState({
          items : items,  //リストに表示するデータ
          loading: false, // 読み込み中を完了する
          lastVisible:lastVisible, // 現在読み込んでいる最後のデータを保持する(次に読み込むとき、「これより後」と絞る)
     })
   })



  // 再読み込み リストが最下部に到着したら読み込み
  fetchMore =async  () => {
      if(this.state.refreshing){ // すでにrefreshingが実行されていたら抑止する 注釈1)
      console.log("二重読み込み抑止")
      return
    }
    console.log('追加読み込み開始');
    this.setState({
        refreshing: true,
    });

       const firestore = firebase.firestore();
    let additionalQuery = await firestore.collection("コレクション")
     .orderBy("createdAt", "desc")
     // 前回読み込んだ最後をカーソルに設定し、そこから、limitまで取得する
    // orderByで定義した方向にlimit分取得する
    // lastVisibleは、orderByで定義した項目と同じものを設定する
     .startAfter(this.state.lastVisible)
     .limit(limit)
    
     let documentSnapshots = await additionalQuery.get()
     let items = []
     documentSnapshots.forEach(function(doc) {
       var res = doc.data();    
       items.push(res);
     })
     // 追加取得したデータの最後のデータをstateに記録する
     // orderBy("createdAt", "desc") としているのでcreatedAtを保持する
     let lastVisible = items[items.length - 1].createdAt;
     // 取得したデータを、既存のリストのデータの後ろにつける
     let itemsList = this.state.items
     for(let item of items) {
       itemsList.push(item)
     }
     // stateを更新し、loding状態を終了する
     this.setState({
       items : itemsList,
       loading: false,
       lastVisible:lastVisible,
       refleshing:false // リフレッシュ状態を終了
     })
  }

// ローディングのインジケータ 下部ページング
  renderFooter = () => {
    if (this.state.refreshing) {
      return <ActivityIndicator size="large" />;
    } else {
      return null;
    }
  };

  render() {

    return(
      <View>
        <FlatList
        onEndReached={this.fetchMore} // 表示末端まで到達時に、呼び出し
        onEndReachedThreshold={0.1}     // 感度を指定。再読み込みを行う閾値を設定する
        ListFooterComponent={this.renderFooter} //画面下部のインジケータを表示
        refreshing={this.state.refreshing} 
        data={this.state.items}
        extraData={this.state.refreshFlatList}
        renderItem={this.getItem}
        keyExtractor={item => item.id}

         />
      </View>
    )
  }
}
  • 注釈1)
  • リフレッシュした結果、リストに重複した行が挿入されてしまう問題
  • 原因は、インフィネートスクロールの読み込みイベントがダブって実行されてしまっていること。処理が完了してlastVisibleが更新される前に再実行されてしまい、結果が重複する
  • 対策は、すでにローディングに入っていたら、それが完了するまで、ローディング処理に入れないようにする

参考資料

3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?