React Redux react-infinite-scroller で 無限スクロールを実装する


Blog の ページング処理を実装しておらず、通常ページングよりも、無限スクロールのページングのほうがよさそうに思えたので、react-infinite-scroller を使って実装してみました。
結果を記載します。


前提

以下のライブラリを使用しています。

  • react@16.3.2
  • rmwc@1.5.6
  • react-redux@5.0.7
  • redux@3.7.2

React の 無限スクロール ライブラリについて

react infinite scroll で検索すると、幾つかライブラリが見つかります。
正直どれを使えばいいのかは皆目見当がつきませんが、検索結果で一番上に来ていた。react-infinite-scrollerを使用することにしました。
以下はその他、気になったものになります。


インストール、使い方

react-infinite-scroller/README.md at master · CassetteRocks/react-infinite-scroller の記載に従って、作業 進めます。

インストール

npm install react-infinite-scroller --save

使い方

README.md に指定可能なプロパティの記載がありますが、私が使用したプロパティについて説明します。

  • タグの記載方法
    以下の通り、タグは記載しました。
    タグ内に、繰り返し呼び出したい処理を記載します。{this.renderPosts()} が繰り返し呼び出されます。

    <InfiniteScroll
        pageStart={0}
        loadMore={this.fetchAndFilterPosts.bind(this)}
        hasMore={this.props.total_count >= (this.props.current_page * 20)}
        initialLoad={true}
        loader={<LinearProgress key={0} determinate={false} progress={0.6} buffer={0.8}></LinearProgress>}>
        {this.renderPosts()}
    </InfiniteScroll>
    

  • プロパティの説明

    • pageStart
      初回ロード時のページインデックスを指定します。

    • loadMore
      ロード時に、呼び出す function を指定します。
      ページのインデックスは指定していなくても、ライブラリ側で、ページインデックスを渡してくれる作りになっており、これを知らずに、index を指定していましたが、動きませんでした。
      index の指定箇所は、react-infinite-scroller/InfiniteScroll.js at master · CassetteRocks/react-infinite-scroller に記述されている以下になります。

        if (offset < Number(this.props.threshold)) {
        this.detachScrollListener();
        // Call loadMore after detachScrollListener to allow for non-async loadMore functions
        if (typeof this.props.loadMore === 'function') {
          this.props.loadMore((this.pageLoaded += 1));
        }
      }
      

    • hasMore
      次のロードが必要な要素が存在する場合は、true 、 存在しない場合、false を返すように実装します。

    • initialLoad
      初回、loadMore で定義した function を呼び出すかどうかを指定します。
      true だと呼び出されます。

    • loader
      load 時に表示する Component を指定します。
      rmwcLinearProgress という Progress バーを出力するコンポーネントがありますので、それを使用しています。


Redux 使用時に気をつける点

Redux を使用しているのですが、以下、気づかずにはまったので気をつける点として記載します。

  • componentWillMount 等で、 loadMore で指定した function を呼び出している場合は、削除する
    initialLoadtrue 指定している場合は、初回勝手に呼び出されるため、削除する必要があります。
    initialLoadfalse にして、明示的に呼び出してもいいかと思いますが、冗長に思えたので削除しました。

    //    componentWillMount() {
    //         this.fetchAndFilterPosts(this.props.current_page);
    //    }
    

  • loadMore で指定した function 内で 前回取得データとのマージ処理を行う
    次ページのデータを load すると、前回取得したデータが消えてしまうので、明示的にマージする必要があります。
    私は reducer の処理で、前回取得データとのマージを実施するようにしました。
    無条件にマージなので、消えて欲しいタイミングで削除するという考慮が別途必要になるかもしれません。

    • reducer_posts.js
      import {FETCH_POSTS, FETCH_POST} from "../actions/index";
      
      /* List of all posts and an active post  */
      const INITIAL_STATE = {
          posts: [],
          total_count: 100000,
          current_page : 0,
          post: null
      };
      
      export default function (state = INITIAL_STATE, action) {
          switch (action.type) {
          case FETCH_POST:
              return {...state, post: action.payload.data.items[0]};
          case FETCH_POSTS:
              return {...state,
                  // post データを前回取得したデータにconcat で追加する
                  posts: state.posts.concat(action.payload.data.items),
                  total_count: action.payload.data.meta.total_count,
                  current_page: action.payload.data.meta.current_page
              };
          default:
              return state;
          }
      }
      

参考

JavaScript のライブラリは基本的にどれを使えばいいのかよくわかりませんが、無限スクロールは輪をかけてどれを使っていいのかがわかりませんでした。
以上です。

コメント