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 を指定します。
      rmwc LinearProgressいう Progress バーを出力するコンポーネントがありますので、それを使用しています。


Redux 使用時に気をつける

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

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

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

コメント