はじめに
この記事は、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()
は、x
と boundaryCount + 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
に関しては答え合わせ程度にとどめ、最後まで読んで次回で最終回としたいと思います!
コメント