MUIの usePagination のロジックを読む3

目次

はじめに

この記事は、MUI が提供している usePagination というカスタムフックのコードリーディング第三回です。

前回、前々回の記事をまだご覧になっていない方は、そちらも併せてご覧ください。

前回は、usePagination フックを実際に使ってみました。
そしてその戻り値から、総件数と現在ページを渡すだけで、いい感じに省略表示したページの配列を返してくれるということがわかりました。

今回は、その配列を作るための処理をコードリーディングしてみたいと思います。

概要

以下は、ページの配列を作っている箇所を抜粋したコードです。

大きく二つに分けることができます。
前半はページの配列を部分ごとに作る処理、後半はそれらを結合する処理です。

  const range = (start, end) => {
    const length = end - start + 1;
    return Array.from({ length }, (_, i) => start + i);
  };

  const startPages = range(1, Math.min(boundaryCount, count));
  const endPages = range(Math.max(count - boundaryCount + 1, boundaryCount + 1), count);

  const siblingsStart = Math.max(
    Math.min(
      // Natural start
      page - siblingCount,
      // Lower boundary when page is high
      count - boundaryCount - siblingCount * 2 - 1,
    ),
    // Greater than startPages
    boundaryCount + 2,
  );

  const siblingsEnd = Math.min(
    Math.max(
      // Natural end
      page + siblingCount,
      // Upper boundary when page is low
      boundaryCount + siblingCount * 2 + 2,
    ),
    // Less than endPages
    count - boundaryCount - 1,
  );

  // Basic list of items to render
  // for example itemList = ['first', 'previous', 1, 'ellipsis', 4, 5, 6, 'ellipsis', 10, 'next', 'last']
  const itemList = [
    ...(showFirstButton ? ['first'] : []),
    ...(hidePrevButton ? [] : ['previous']),
    ...startPages,

    // Start ellipsis
    // eslint-disable-next-line no-nested-ternary
    ...(siblingsStart > boundaryCount + 2
      ? ['start-ellipsis']
      : boundaryCount + 1 < count - boundaryCount
        ? [boundaryCount + 1]
        : []),

    // Sibling pages
    ...range(siblingsStart, siblingsEnd),

    // End ellipsis
    // eslint-disable-next-line no-nested-ternary
    ...(siblingsEnd < count - boundaryCount - 1
      ? ['end-ellipsis']
      : count - boundaryCount > boundaryCount
        ? [count - boundaryCount]
        : []),

    ...endPages,
    ...(hideNextButton ? [] : ['next']),
    ...(showLastButton ? ['last'] : []),
  ];

一つずつ見ていきましょう。

部分ごとの解説

連番を作成する関数 range

ページャのボタンは、連続している箇所は必ず連番になります。
この処理は、その連番部分を作成します。

引数のstartend の値から連番の範囲 length を求めます。
そして、その範囲の分だけ連番の配列を作成します。

  const range = (start, end) => {
    const length = end - start + 1;
    return Array.from({ length }, (_, i) => start + i);
  };

例として、start=1 end=3 を渡した場合、以下のような出力になります。

console.log(range(1, 3))
// [1, 2, 3]

始めのページと終わりのページ

  const startPages = range(1, Math.min(boundaryCount, count));
  const endPages = range(Math.max(count - boundaryCount + 1, boundaryCount + 1), count);

いきなりややこしいですが、ひるまず一つずつ見ていきましょう。

startPages は、1から boundaryCountcount の小さい方までの連番となります。

例えば、boundaryCount が 1 で、count が 3 のとき、range(1, 1) となり、出力は [ 1 ] となります。
同様に、boundaryCount が 2 で、count が 3 のとき、range(1, 2) となり、出力は [ 1, 2 ] となります。
もう一つ見てみましょう。boundaryCount が 5 で、count が 10 のとき、range(1, 5) となり、出力は [ 1, 2, 3, 4, 5 ] となります。
最後です。boundaryCount が 5 で、count が 2 のとき、range(1, 2) となり、出力は [ 1, 2 ] となります。

この結果から startPages は、1から boundaryCount までの範囲、ただし boundaryCountcount を超える場合は count までの範囲となることがわかります。

続いて、endPages です。startPages よりさらにややこしいですね。
先ほどのように、実際に値を当てはめて確認していきましょう。

例えば、boundaryCount が 1 で、count が 10 のとき、range(10, 10) となり、出力は [ 10 ] となります。
同様に、boundaryCount が 2 で、count が 5 のとき、range(4, 5) となり、出力は [ 4, 5 ] となります。
もう一つ見てみましょう。boundaryCount が 5 で、count が 10 のとき、range(6, 10) となり、出力は [ 6, 7, 8, 9, 10 ] となります。
最後です。boundaryCount が 5 で、count が 2 のとき、range(6, 2) となり、出力は [] となります。

この結果から endPages は、count - boundaryCount + 1 から count までの範囲、ただし count がある一定より少ない場合は boundaryCount + 1 までの範囲となることがわかります。
さらに、最後の例にあるとおり、場合によっては空配列となる場合もあります。

これらの結果からboundaryCount「ページャの両端からボタンを表示する数」だと考えてよさそうですね。
そして、boundaryCount に大きな値が渡された場合の考慮もされていることがわかりました。

おわりに

いかがだったでしょうか?

第1回の記事で、boundary=境界なのでboundaryCount は省略表示に関係しているのでは?と予想していました。
答え合わせとしては「両端から省略記号まで表示する数」でしたね。

次回も残りの処理を紐解いていきましょう。

第1回の記事で想像するのも難しかった siblingCount が登場します。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

28歳のときに音楽家からエンジニアに転向。
WEB系のシステム開発(Java, C#, Vue, React など)に携わっています。

自分らしく力を発揮できて、自信を持って生きられる人を増やすための活動をしています。

コメント

コメントする

目次