← Blog一覧へ

2024-01-01

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

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

ページャー

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

考え方

  • 基本は、総ページ数分のページ番号を表示する。
  • ただしページ数が多い場合は、最初、最後、現在のページとその隣のページ以外については「…」で省略する
  • 「前へ」「次へ」を表示して、現在のページを増減させた値でイベントを発生させる

// 現在のページの両隣に表示するページ数
const PAGER_NEIGHBORS = 2;

type Props = {
  currentPage: number;
  totalPages: number;
  handlePageChange: (_page: number) => void;
}

const PageList = ({
  currentPage,
  totalPages,
  handlePageChange,
}: Props) => {
  const pages = [];
  for (let i = 1; i <= totalPages; i++) {
    // 2ページ目と最後のページの手前に「...」を差し込む
    if (
      (i === 2 && currentPage > PAGER_NEIGHBORS + 2) ||
      (i === totalPages && currentPage < totalPages - (PAGER_NEIGHBORS + 1))
    ) {
      pages.push(
        <li className="ellipse">...</li>
      )
    }
    // ページ番号を表示(最初のページ、最後のページ、現在のページから両隣の定数分表示する)
    if (i === 1 || i === totalPage || Math.abs(currentPage + 1) <= PAGER_NEIGHBORS) {
      pages.push(
        <li
          className={`page ${i === currentPage && 'current'}`}
          onClick={() => handlePageChange(i)}
        >
        {i}
        </li>
      )
    }
  }
  return (
    <ul>
      <li
        className="pager previous"
        onClick={() => handlePageChange(
          currentPage > 1 ? currentPage - 1 : 1
        )}
      >前へ</li>
      {pages}
      <li
        className="pager next"
        onClick={() => handlePageChange(
          currentPage < totalPages ? currentPage + 1 : totalPages
        )}
      >次へ</li>
    </ul>
  )
}

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

モーダル

考え方

  • ウィンドウ全体を覆う半透明な背景を用意する。
  • その上の中心にモーダルを実装する。
  • モーダル以外をクリックしたら閉じる(isCancelableがtrueの場合)
type Props = {
  isOpen: boolean;
  children: React.ReactNode;
  handleClose: () => void;
  isCancelable: boolean;
}


const Modal = ({
  isOpen,
  children,
  handleClose,
  isCancelable,
}: Props) => {
  return (
    <>
      {isOpen && (
        <div
          className="wrapper"
          onClick={() => {
            isCancelable && handleClose();
          }}
        >
          <div className="modal">
            {children}
          </div>
        </div>
      )}
    </>
  )
}