2024-01-01 投稿

Reactコンポーネントの実装カタログ

Reactフロントエンド開発の素養を深めるため、車輪の再発明ではあるが、フロントでよく使われるコンポーネントの実装を行います。

ページャー

ページ自体の表示のほかに、ページ送り、ページの省略表示といったことを考える必要があります。

考え方

  • 基本は、総ページ数分のページ番号を表示する。
  • ただしページ数が多い場合は、最初、最後、現在のページとその隣のページ以外については「…」で省略する
  • 「前へ」「次へ」を表示して、現在のページを増減させた値でイベントを発生させる
1// 現在のページの両隣に表示するページ数
2const PAGER_NEIGHBORS = 2;
3
4type Props = {
5  currentPage: number;
6  totalPages: number;
7  handlePageChange: (_page: number) => void;
8}
9
10const PageList = ({
11  currentPage,
12  totalPages,
13  handlePageChange,
14}: Props) => {
15  const pages = [];
16  for (let i = 1; i <= totalPages; i++) {
17    // 2ページ目と最後のページの手前に「...」を差し込む
18    if (
19      (i === 2 && currentPage > PAGER_NEIGHBORS + 2) ||
20      (i === totalPages && currentPage < totalPages - (PAGER_NEIGHBORS + 1))
21    ) {
22      pages.push(
23        <li className="ellipse">...</li>
24      )
25    }
26    // ページ番号を表示(最初のページ、最後のページ、現在のページから両隣の定数分表示する)
27    if (i === 1 || i === totalPage || Math.abs(currentPage + 1) <= PAGER_NEIGHBORS) {
28      pages.push(
29        <li
30          className={`page ${i === currentPage && 'current'}`}
31          onClick={() => handlePageChange(i)}
32        >
33        {i}
34        </li>
35      )
36    }
37  }
38  return (
39    <ul>
40      <li
41        className="pager previous"
42        onClick={() => handlePageChange(
43          currentPage > 1 ? currentPage - 1 : 1
44        )}
45      >前へ</li>
46      {pages}
47      <li
48        className="pager next"
49        onClick={() => handlePageChange(
50          currentPage < totalPages ? currentPage + 1 : totalPages
51        )}
52      >次へ</li>
53    </ul>
54  )
55}

地味だが、前後◯ページとの比較をするために、Math.absを使っているところが工夫ポイント(比較演算子の数が減る)。

モーダル

考え方

  • ウィンドウ全体を覆う半透明な背景を用意する。
  • その上の中心にモーダルを実装する。
  • モーダル以外をクリックしたら閉じる(isCancelableがtrueの場合)
1type Props = {
2  isOpen: boolean;
3  children: React.ReactNode;
4  handleClose: () => void;
5  isCancelable: boolean;
6}
7
8
9const Modal = ({
10  isOpen,
11  children,
12  handleClose,
13  isCancelable,
14}: Props) => {
15  return (
16    <>
17      {isOpen && (
18        <div
19          className="wrapper"
20          onClick={() => {
21            isCancelable && handleClose();
22          }}
23        >
24          <div className="modal">
25            {children}
26          </div>
27        </div>
28      )}
29    </>
30  )
31}