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

目次

はじめに

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

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

前回は、usePagination フックを実際にコードリーディングしてみました。
そして、ページャを表示するための配列の両端部分を作成する処理を読むことができました。

今回は、続きの処理をコードリーディングしたいと想います。

概要

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

今回は、前回の続きの47行目から読み進めていきます。

  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'] : []),
  ];

部分ごとの解説

siblingsStart とは?

第一回の記事で usePagination の引数から処理を想像する、ということをしました。
そこでは大体のことが想像できましたが、sibling=兄弟 という言葉がいまいちよくわかりませんでした。

ついにその言葉が使われている siblingStart が登場しました。

処理としても、パッと見よくわかりませんね。

でも大丈夫。落ち着いて読み進めていきましょう。

  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,
  );

このような複雑なコードを目の前にしたときのコツをお伝えします。

それは「仮置きしてシンプル化する」です。

何を言っているかよくわからないと思うので、実際にやってみましょう。

ここで使われている Math.max() Math.min() は、0個以上の引数のうち一番大きいもの、あるいは一番小さいものを返す関数です。

このコードはMath.max() の中にMath.min() がネストされていてわかりにくいので、いったん中の Math.min() を以下のように x で仮置きしてシンプル化してみましょう。

  const siblingsStart = Math.max(
    x,
    // Greater than startPages
    boundaryCount + 2,
  );

これで外側の Math.max() は、xboundaryCount + 2 のどちらか大きい方を返している、ということがわかります。

次は、x で置き換えた Math.min() の処理を紐解いていきましょう。

    Math.min(
      // Natural start
      page - siblingCount,
      // Lower boundary when page is high
      count - boundaryCount - siblingCount * 2 - 1,
    ),

Math.min() なので、2つの引数のうち小さい方を返しています。
片方ずつ見ていきましょう。

page - siblingCount はわかっているところまで日本語にすると「現在ページ – siblingCount」

count - boundaryCount - siblingCount * 2 - 1 も、わかっているところまで日本語にしてみましょう。
「総ページ数- 両端から省略記号まで表示する数 – siblingCount * 2 – 1」

後者がまだいまいちピンとこないですが、総ページ数から両端の数やら何やらを引いています。
コメントにある「page が high のときの Lower boundary」ということなので、現在ページが終わりに近いときの場合が想定されているようです。

設定を色々変えて実際に動かしてみたところ、総ページ数が30で boundaryCount=3、現在ページが 27 のときにこの値が使われることがわかりました。
これは言葉で頑張って説明するよりも動いているのを見た方が早いと思うので、スクショをご覧ください。

このように、総ページ数とページ数がともに大きい値で、かつ boundaryCount もある一定の大きさの値が指定されている場合を想定した処理のようです。

ここまでで、内側の Math.min()「現在ページから手前の省略記号まで」を求めるための処理であることがわかりました。

内側の Math.min() の処理が紐解けたので、外側の Math.max() で置き換えた x に当てはめてみましょう。

  const siblingsStart = Math.max(
    「現在ページから手前の省略記号まで」,
    // Greater than startPages
    boundaryCount + 2,
  );

siblingsStart は、現在ページから手前の省略記号までboundaryCount - 2 のどちらか大きい方の値、であることがわかりました。

つまり「現在ページから手前の省略記号まで」と「両端から省略記号まで表示する数 – 2」のどちらか大きい方、ということになります。

おわりに

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

今回は、現在ページから手前の省略記号までを表示するための処理をコードリーディングしました。

コードリーディングをするときのコツとして、「複雑でわかりにくい箇所は仮置きしてシンプル化する」ということをお伝えしました。

書かれているコードを素直に読む以外にも、理解できるようにするためのアプローチはいくつかあります。

コードリーディングがはかどらないときは、今回やったアプローチを試してみてください。

次回は、siblingsEnd の処理からコードリーディングをしていきます。

siblingsEndはその名前のとおりsiblingsStart と対応しているものだと思います。
なので、おそらく「現在ページから後ろの省略記号まで」ということになるでしょう。

siblingsEnd に関しては答え合わせ程度にとどめ、最後まで読んで次回で最終回としたいと思います!

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

この記事を書いた人

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

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

コメント

コメントする

目次