ike81818
@ike81818

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

冗長的なコードを関数や別ファイルにまとめたいです

解決したいこと

Next.jsを使って、下記のようなコードを書いたのですが、
ページングの部分がコンポーネント化できず、
(子コンポーネントから親コンポーネントへ「ページ番号」を渡すことができないため)
pages/index.jsに直接書きました。
ページングナビは、サイトの上下に2つあるので、同じ長いコードを2つ書きました。
この重複する2つのコード部分を関数化したり、別ファイルにして読み込んだりする方法はあるのでしょうか?
ご教示いただけますと幸いです。

[サイト] https://zesty-faun-47f010.netlify.app/

コード

pages/index.js
import Head from "next/head";
import { useState, useEffect } from "react";
import styles from "../styles/Home.module.css";
import Title from "../components/title";
import Form from "../components/form";
import Sidebar from "../components/sidebar";
import Results from "../components/results";
import Footer from "../components/footer";

export default function Home() {
  const [keyword, setKeyword] = useState(""); //指定キーワード
  const [category, setCategory] = useState(""); //指定カテゴリ
  const [page, setPage] = useState(1); //現在のページ
  const [resultsPerPage, setResultsPerPage] = useState(10); //1ページあたりのitem数

  const [items, setItems] = useState([]); //アイテムの配列
  const [pageCount, setPageCount] = useState(""); //総ページ数

  const options = {
      params: {
      keyword: keyword,
      category: category,
      page: page,
      results_per_page: resultsPerPage,
    },
  };

  let readMoreFlg = false;

  const getItems = async () => {
    const res = await axios.get("/api/items", options);
    // 検索結果をセット
    if (options.params.page == 1 || readMoreFlg == false) {
      setItems(res.data.users.items); //アイテム群をセット
    } else {
      setItems((prev) => {
        return [...prev, ...res.data.users.items];
      });
    }
    setPageCount(res.data.users.pageCount); //総ページ数をセット
  };

  useEffect(() => {
    getItems();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Pagination +
  const pageIncrement = () => {
    if (page < pageCount) {
      const newPage = page + 1;
      setPage((prevPage) => prevPage + 1);
      options.params.page = newPage;
      readMoreFlg = false;
      getItems();
    }
  };

  // Pagination -
  const pageDecrement = () => {
    if (page > 1) {
      const newPage = page - 1;
      setPage((prevPage) => prevPage - 1);
      options.params.page = newPage;
      readMoreFlg = false;
      getItems();
    }
  };

  const pageChange = (newPage) => {
    setPage(newPage);
    options.params.page = newPage;
    readMoreFlg = false;
    getItems();
  };

  return (
    <div className={styles.zentai}>
      <div className={styles.container}>
        <Head>
          <title>総合ショップ</title>
          {/* <meta name="viewport" content="width=device-width,initial-scale=1.0" /> */}
          {/* <meta name="viewport" content="viewport-fit=cover" /> */}
        </Head>
        <div id="page_top"></div>
        <div className={styles.top}>
          <Title reload={reload} />
          <Form
            category={category}
            setKeyword={setKeyword}
            setCategory={setCategory}
            searchItems={searchItems}
            keyword={keyword}
          />
          {/* <div style={{ marginTop: "24px" }}> */}
        </div>

// ↓ ここからが 上部のページネーションナビ
        <div className={styles.paginationT}>
          {(() => {
            if (pageCount > 0) {
              let haba = 2;
              let startpage = page - haba;
              if (startpage < 2) {
                startpage = 2;
              }
              let endpage = page + haba;
              if (endpage >= pageCount) {
                endpage = pageCount - 1;
              }
              pageCount);

              const items = [];

              if (page > 2) {
                items.push(
                  <button
                    className={styles.pageButton}
                    onClick={() => pageDecrement()}
                  >
                    前へ
                  </button>
                );
              }

              items.push(
                <button
                  className={page == 1 ? styles.pageButtonC : styles.pageButton}
                  onClick={() => pageChange(1)}
                >
                  1
                </button>
              );

              if (startpage >= 3) {
                items.push(<button className={styles.pageButton}>...</button>);
              }
              for (let i = startpage; i <= endpage; i++) {
                items.push(
                  <button
                    className={
                      i == page ? styles.pageButtonC : styles.pageButton
                    }
                    onClick={() => pageChange(i)}
                  >
                    {i}
                  </button>
                );
              }
              if (endpage <= pageCount - 2) {
                items.push(<button className={styles.pageButton}>...</button>);
              }
              if (pageCount > 1) {
                items.push(
                  <button
                    className={
                      page == pageCount ? styles.pageButtonC : styles.pageButton
                    }
                    onClick={() => pageChange(pageCount)}
                  >
                    {pageCount}
                  </button>
                );
              }
              if (page < pageCount) {
                items.push(
                  <button
                    className={styles.pageButton}
                    onClick={() => pageIncrement()}
                  >
                    次へ
                  </button>
                );
              }
              return <div>{items}</div>;
            }
          })()}

          <div className={styles.pagination2}>
            {page} / {pageCount} ペー          </div>
        </div>
// ↑ ここまで 上部ページネーションナビ

// サイドバー
        <Sidebar
          category={category}
          setCategory={setCategory}
          categoryItems={categoryItems}
        />
//メインコンテンツ
        <Results items={items} keyword={keyword} category={category} />

// ↓ ここから 下部のページネーションナビ
        <div className={styles.paginationB}>
          {(() => {
            if (pageCount > 0) {
              let haba = 2;
              let startpage = page - haba;
              if (startpage < 2) {
                startpage = 2;
              }
              let endpage = page + haba;
              if (endpage >= pageCount) {
                endpage = pageCount - 1;
              }

              const items = [];

              if (page > 2) {
                items.push(
                  <button
                    className={styles.pageButton}
                    onClick={() => pageDecrement()}
                  >
                    前へ
                  </button>
                );
              }

              items.push(
                <button
                  className={page == 1 ? styles.pageButtonC : styles.pageButton}
                  onClick={() => pageChange(1)}
                >
                  1
                </button>
              );

              if (startpage >= 3) {
                items.push(<button className={styles.pageButton}>...</button>);
              }
              for (let i = startpage; i <= endpage; i++) {
                items.push(
                  <button
                    className={
                      i == page ? styles.pageButtonC : styles.pageButton
                    }
                    onClick={() => pageChange(i)}
                  >
                    {i}
                  </button>
                );
              }
              if (endpage <= pageCount - 2) {
                items.push(<button className={styles.pageButton}>...</button>);
              }
              if (pageCount > 1) {
                items.push(
                  <button
                    className={
                      page == pageCount ? styles.pageButtonC : styles.pageButton
                    }
                    onClick={() => pageChange(pageCount)}
                  >
                    {pageCount}
                  </button>
                );
              }
              if (page < pageCount) {
                items.push(
                  <button
                    className={styles.pageButton}
                    onClick={() => pageIncrement()}
                  >
                    次へ
                  </button>
                );
              }
              return <div>{items}</div>;
            }
          })()}

          <div className={styles.pagination2}>
            {page} / {pageCount} ペー          </div>
        </div>

// ↑ ここまでが 下部のページネーションナビ

//フッター
        <Footer />

       </div>
    </div>
  );
}

下記のようなコードで一応できました

pages/index.js
import Head from "next/head";
import { useState, useEffect } from "react";
import styles from "../styles/Home.module.css";
import Title from "../components/title";
import Form from "../components/form";
import Sidebar from "../components/sidebar";
import Results from "../components/results";
import Footer from "../components/footer";

import Paging from "../components/paging";

export default function Home() {
  const defaultKeyword = ["", "", ""];
  const randomKeyword =
    defaultKeyword[Math.floor(Math.random() * defaultKeyword.length)];

  const [keyword, setKeyword] = useState(randomKeyword); //指定キーワード
  const [category, setCategory] = useState(""); //指定カテゴリ
  const [page, setPage] = useState(1); //現在のページ
  const [resultsPerPage, setResultsPerPage] = useState(10); //1ページあたりのitem数

  const [items, setItems] = useState([]); //アイテムの配列
  const [pageCount, setPageCount] = useState(""); //総ページ数

  const options = {
    params: {
      keyword: keyword,
      category: category,
      page: page,
      results_per_page: resultsPerPage,
    },
  };

  let readMoreFlg = false;

  const getItems = async () => {
    const res = await axios.get("/api/items", options);
    // 検索結果をセット
    if (options.params.page == 1 || readMoreFlg == false) {
      setItems(res.data.users.items); //アイテム群をセット
    } else {
      setItems((prev) => {
        return [...prev, ...res.data.users.items];
      });
    }
    setPageCount(res.data.users.pageCount); //総ページ数をセット
  };

  useEffect(() => {
    getItems();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Search
  const searchItems = (e) => {
    e.preventDefault(); // リロードを止める
    setPage(1);
    options.params.page = 1;
    getItems();
    // scroll_to_top();
  };

  // Search
  const categoryItems = (categorye) => {
    // e.preventDefault(); // リロードを止める
    setPage(1);
    setCategory(categorye);
    options.params.page = 1;
    options.params.category = categorye;
    getItems();
    // scroll_to_top();
  };

  const reload = (e) => {
    location.reload();
  };

  const scroll_to_top = () => {
    window.scroll({ top: 0, behavior: "auto" });
  };

   // Pagination +
  const pageIncrement = () => {
    if (page < pageCount) {
      const newPage = page + 1;
      // setPage(newPage);
      setPage((prevPage) => prevPage + 1);
      options.params.page = newPage;
      readMoreFlg = false;
      getItems();
      // scroll_to_top();
    }
  };

  // Pagination -
  const pageDecrement = () => {
    if (page > 1) {
      const newPage = page - 1;
      // setPage(newPage);
      setPage((prevPage) => prevPage - 1);
      options.params.page = newPage;
      readMoreFlg = false;
      getItems();
      // scroll_to_top();
    }
  };

  const pageChange = (newPage) => {
    setPage(newPage);
    options.params.page = newPage;
    readMoreFlg = false;
    getItems();
  };

  return (
    <div className={styles.zentai}>
      <div className={styles.container}>
        <Head>
          <title>総合ショップ</title>
          {/* <meta name="viewport" content="width=device-width,initial-scale=1.0" /> */}
          {/* <meta name="viewport" content="viewport-fit=cover" /> */}
        </Head>
        <div id="page_top"></div>
        <div className={styles.top}>
          <Title reload={reload} />
          <Form
            category={category}
            setKeyword={setKeyword}
            setCategory={setCategory}
            searchItems={searchItems}
            keyword={keyword}
          />
          {/* <div style={{ marginTop: "24px" }}> */}
        </div>

        <div className={styles.paginationT}>
          <Paging
            pageIncrement={pageIncrement}
            pageDecrement={pageDecrement}
            pageChange={pageChange}
            page={page}
            pageCount={pageCount}
          />
        </div>

        {/* <div className={styles.container2}> */}
        {/* <Searchkey keyword={keyword} category={category} /> */}

        <Sidebar
          category={category}
          setCategory={setCategory}
          categoryItems={categoryItems}
        />
        <Results items={items} keyword={keyword} category={category} />

        <div className={styles.paginationB}>
          <Paging
            pageIncrement={pageIncrement}
            pageDecrement={pageDecrement}
            pageChange={pageChange}
            page={page}
            pageCount={pageCount}
          />
        </div>

        <Footer />

        <a href="#page_top" className={styles.page_top_btn}>
          トップへ戻る
        </a>
      </div>
    </div>
  );
}
components/paging.js
import styles from "../styles/Home.module.css";

const Paging = ({
  pageIncrement,
  pageDecrement,
  pageChange,
  page,
  pageCount,
}) => {
  let pageCount2 = pageCount;
  if (pageCount2 > 1000) {
    pageCount2 = 1000;
  }
  let haba = 2;
  let startpage = page - haba;
  if (startpage < 2) {
    startpage = 2;
  }
  let endpage = page + haba;
  if (endpage >= pageCount2) {
    endpage = pageCount2 - 1;
  }

  const items = [];

  if (page > 2) {
    items.push(
      <button className={styles.prevPageButton} onClick={() => pageDecrement()}>
        前へ
      </button>
    );
  }

  if (pageCount2 > 1) {
    items.push(
      <button
        className={page == 1 ? styles.pageButtonC : styles.pageButton}
        onClick={() => pageChange(1)}
      >
        1
      </button>
    );
  }

  if (startpage >= 3) {
    items.push(<span className={styles.pageSpan}>...</span>);
  }
  for (let i = startpage; i <= endpage; i++) {
    items.push(
      <button
        className={i == page ? styles.pageButtonC : styles.pageButton}
        onClick={() => pageChange(i)}
      >
        {i}
      </button>
    );
  }
  if (endpage <= pageCount2 - 2) {
    items.push(<span className={styles.pageSpan}>...</span>);
  }
  if (pageCount2 > 1) {
    items.push(
      <button
        className={page == pageCount2 ? styles.pageButtonC : styles.pageButton}
        onClick={() => pageChange(pageCount2)}
      >
        {pageCount2}
      </button>
    );
  }
  if (page < pageCount2) {
    items.push(
      <button className={styles.nextPageButton} onClick={() => pageIncrement()}>
        次へ
      </button>
    );
  }
  return (
    <>
      <div>{items}</div>
      <div className={styles.pagination2}>
        {page} / {pageCount > 1000 ? 1000 : pageCount} ペー      </div>
    </>
  );
};

export default Paging;
0

1Answer

Comments

  1. @ike81818

    Questioner

    ご回答ありがとうございます。
    pagingコンポーネントを作って、各関数を渡しました。
    上記投稿に修正後のコードを追記し、サイトリンクも修正後のものに差し替えました。
    今のところ、動作は正常と思われます。
    ありがとうございました。

Your answer might help someone💌