2023.11.13 커스텀 훅, 디테일 페이지 완성

이무헌·2023년 11월 13일
0

block explore

목록 보기
4/6

1.페이지네이션 커스텀 훅


import React, { useEffect, useState } from "react";
import { ItxList } from "@app/_components/transactionTable/ineterface";
import { Iitem } from "@app/(page)/nft/collections/interface";
const usePagination = (txList: any) => {
  const [page, setPage] = useState<number>(1);
  const [pageTxList, setPageTxList] = useState<ItxList[] | Iitem[] | null>(
    null
  );
  const [maxPage, setMaxPage] = useState<number>(0);

  const pageHandler = (selectedPage: number) => {
    if (selectedPage > 0 && selectedPage < maxPage + 1) {
      setPage(selectedPage);
    }
  };
  useEffect(() => {
    setMaxPage(Math.ceil((txList?.length as number) / 25));
    if (page === maxPage) {
      setPageTxList(txList?.slice((page - 1) * 25));
    } else {
      setPageTxList(txList?.slice((page - 1) * 25, page * 25));
    }
  }, [page, txList, maxPage]);
  return {
    page,
    pageHandler,
    maxPage,
    pageTxList,
  };
};

export default usePagination;
  • 페이지네이션을 관리해주는 자작 커스텀 훅이다.
  • page는 현재 페이지, maxPage 최대 페이지 수 ,pageTxList는 데이터 배열이 매개변수로 주어졌을 때 25개씩 정제된 데이터들이다.
  • 즉,pageTxList를 map으로 출력시켜주면 된다.

예시)

/* eslint-disable @next/next/no-img-element */
"use client";
import React, { useEffect, useState } from "react";
import Image from "next/image";
import { IAddInfo, ItxList, TxListProps } from "./ineterface";
import { createPortal } from "react-dom";
import AdditionalInfo from "./additionalInfo";
import TxList from "./txListContainer";
import usePagination from "@app/_hooks/usePagination";
import Pagination from "../pagination/Pagiation";

const TxListWrap: React.FC<TxListProps> = ({ txList }) => {
  const [addInfoModal, setAddInfoModal] = useState<Element | null>(null);
  const [isToggled, setIsToggled] = useState<boolean>(false);
  const { maxPage, page, pageHandler, pageTxList } = usePagination(txList);

  useEffect(() => {
    setAddInfoModal(document.getElementById("portal"));
  }, [isToggled]);

  // 실제에서는 react-query로 가져올 예정
  const [addInfoTempData, setAddInfoTempData] = useState<IAddInfo>({
    status: "Success",
    transactionFee: "0.0000000000231",
    gasInfo: "293840",
    gasLimit: "318840",
    nonce: "0",
    blockNum: "18497",
    position: "18",
  });

  const toggleHandler = () => {
    setIsToggled(!isToggled);
  };

  return (
    <div className="mt-10  h-auto  max-h-[600px]">
      <div className=" overflow-x-auto h-auto  max-h-[500px]">
        <div className="w-[300%] h-8 border-b border-gray flex ">
          <div className="w-[5%] flex justify-center items-center ">?</div>
          <div className=" w-[20%] flex justify-center items-center  ">
            Txn Hash
          </div>
          <div className=" w-[10%] flex justify-center items-center  ">
            Method
          </div>
          <div className=" w-[10%] flex justify-center items-center  ">
            Block
          </div>
          <div className=" w-[10%] flex justify-center items-center  ">Age</div>
          <div className=" w-[20%] flex justify-center items-center  ">
            From
          </div>
          <div className=" w-[20%] flex justify-center items-center  ">To</div>
          <div className=" w-[10%] flex justify-center items-center  ">
            Value
          </div>
        </div>
        <TxList
          pageTxList={pageTxList as ItxList[]}
          toggleHandler={toggleHandler}
        />

        {isToggled && addInfoModal
          ? createPortal(
              <AdditionalInfo
                addInfoTempData={addInfoTempData}
                toggleHandler={toggleHandler}
              />,
              addInfoModal
            )
          : ""}
     
      </div>
      <Pagination page={page} pageHandler={pageHandler} maxPage={maxPage} />
    </div>
  );
};

export default TxListWrap;
  • TxList 컴포넌트(map이 있는 컴포넌트)에 pageTxList 즉, 25개 인덱스로 slice된 배열을 props로 준다.
  • Pagination 컴포넌트를 원하는 위치에 넣고 각 props 키값을 주면 정상 작동 된다.

2.nft 디테일 페이지

1.page.tsx


"use client";

import React, { useState } from "react";
import ItemTitle from "@app/_components/itemTitle";
import { INftTxDetailProps } from "./interface";
import ItemTableWrap from "@app/_components/itemTable";
import OverViewInfo from "./_contents/OverViewInfo";
import MarketInfo from "./_contents/MarketInfo";
import OtherInfo from "./_contents/OtherInfo";
import TxListWrap from "@app/_components/transactionTable";
import { ItxList } from "@app/_components/transactionTable/ineterface";
import TabWrap from "@app/_components/tabComponent/TabWrap";
import TabButton from "@app/_components/tabComponent/Tab";
import NFTInfo from "./_contents/Info";

const NftTxDetail: React.FC<INftTxDetailProps> = ({ params }) => {
  const tempDataArr: ItxList[] = Array.from({ length: 105 }, (ele, index) => ({
    age: "asd",
    block: "123124124",
    from: "0x12312kj312kjb3jk",
    to: "0xqweqwnekjads2asdk2",
    method: "Transfer",
    txHash: "0x123fjafk231s",
    value: index.toString(),
  }));

  const [toggleLabelNum, setToggleLabelNum] = useState<string | null>(
    "Transfers"
  );

  const toggleHandler = (label: string) => {
    setToggleLabelNum(label);
  };

  const componentHandler = (label: string) => {
    if (label === "Transfers") {
      console.log("Transfers");
      return <TxListWrap txList={tempDataArr} />;
    } else if (label === "Info") {
      return <NFTInfo />;
    } else {
      return <></>;
    }
  };
  return (
    <div className="bg-mainBackGroundColor ">
      <ItemTitle title={params.nftId} />
      <div className="w-20 h-8 m-6 border-2 border-white bg-white rounded-xl font-bold flex justify-center items-center text-sm">
        ERC-721
      </div>
      <ItemTableWrap>
        <OverViewInfo />
      </ItemTableWrap>
      <ItemTableWrap>
        <MarketInfo />
      </ItemTableWrap>
      <ItemTableWrap>
        <OtherInfo />
      </ItemTableWrap>
      <div className="flex overflow-x-auto flex-nowrap py-5">
        <TabButton
          label="Transfers"
          toggleLabelNum={toggleLabelNum}
          toggleHandler={toggleHandler}
        />
        <TabButton
          label="Holders"
          toggleLabelNum={toggleLabelNum}
          toggleHandler={toggleHandler}
        />
        <TabButton
          label="Inventory"
          toggleLabelNum={toggleLabelNum}
          toggleHandler={toggleHandler}
        />
        <TabButton
          label="Info"
          toggleLabelNum={toggleLabelNum}
          toggleHandler={toggleHandler}
        />
      </div>
      {componentHandler(toggleLabelNum as string)}
    </div>
  );
};

export default NftTxDetail;

contents 폴더는 다음과 같다.

1.Holders.tsx

'use client'
import ItemTableWrap from '@app/_components/itemTable'
import usePagination from '@app/_hooks/usePagination'
import React from 'react'

const NFTHolders = () => {
    const tempNftData=Array.from({length:40},()=>{

    })
    // const {}=usePagination()
  return (
   <ItemTableWrap>

   </ItemTableWrap>
  )
}

export default NFTHolders

2.Info.tsx

import React from "react";
import ItemTableWrap from "@app/_components/itemTable";

const NFTInfo = () => {
  return (
    <ItemTableWrap>
      <h1 className=" font-bold text-lg">OverView</h1>
      <p className="mt-5 overflow-y-scroll">
        [Note - Citizens with Reward Rates of 1 do not have Vaults. A Vault can
        be attached to increase Yield for BYTES 2.0 Staking. Citizens with
        Reward Rates of 2 or above have Vaults.] Upload complete. Wake up,
        Citizen. The road ahead is long, so strap in. A new world awaits us. Neo
        Tokyo is a collection of VCs, developers, token founders, and highly
        proficient builders all gathered in one exclusive area of web3. Our
        calling at Neo Tokyo is to prepare and be well positioned at the
        forefront of the metaverse revolution, once it inevitably arrives.
        https://neotokyo.codes
      </p>
    </ItemTableWrap>
  );
};

export default NFTInfo;

3.MaketInfo.tsx

import React from "react";

const MarketInfo = () => {
  return (
    <div className="w-full h-full flex flex-col items-start">
      <div className="m-5 text-lg  font-bold h-1/4">Market</div>
      <div className="m-5  h-1/4">
        <div className="text-itemDetail-textLabelColor">VOLUME(24H)</div>
        <div className="">
          {" "}
          <span>40</span> <span>ETH</span>{" "}
        </div>
      </div>
      <div className="m-5  h-1/4">
        <div className="text-itemDetail-textLabelColor">MIN PRICE(24H)</div>
        <div className="">
          {" "}
          <span>$</span> <span>770,18.03</span> <span>@40.0000000ETH</span>{" "}
        </div>
      </div>
      <div className="m-5  h-1/4">
        <div className="text-itemDetail-textLabelColor">MAX PRICE(24H)</div>
        <div className="">
          <span>$</span> <span>770,18.03</span> <span>@40.0000000ETH</span>
        </div>
      </div>
    </div>
  );
};

export default MarketInfo;

4.OtherInfo.tsx


import Image from "next/image";
import React from "react";
import copy from "../../../../../../public/copy.png";
const OtherInfo = () => {
  return (
    <div className="w-full h-full flex flex-col items-start">
      <div className="m-5 text-lg  font-bold h-1/4">Other Info</div>
      <div className="m-5  h-1/4">
        <div className="text-itemDetail-textLabelColor">TOKEN CONTRACT</div>
        <div className="w-full grid grid-cols-2 gap-4 mt-4 ">
          <Image
            className="flex "
            src={copy}
            alt="종이 아이콘"
            width={15}
            height={3}
          />
          <div className="truncate text-blue-600">0xasdasdkjaqwjhrqwjyrqwjyeasddsa</div>
        </div>
      </div>
    </div>
  );
};

export default OtherInfo;

5.OtherViewInfo.tsx

import React from "react";

const OverViewInfo = () => {
  return (
    <div className="w-full h-full flex flex-col items-start">
      <div className="m-5 text-lg  font-bold h-1/4">Overview</div>
      <div className="m-5  h-1/4">
        <div className="text-itemDetail-textLabelColor">MAX TOTAL SUPPLY</div>
        <div className="">123123</div>
      </div>
      <div className="m-5  h-1/4">
        <div className="text-itemDetail-textLabelColor">HOLDERS</div>
        <div className="">25,301</div>
      </div>
      <div className="m-5  h-1/4">
        <div className="text-itemDetail-textLabelColor">TOTAL TRANSFERS</div>
        <div className="">-</div>
      </div>
    </div>
  );
};

export default OverViewInfo;

3. 느낀점

페이지를 열심히 찍어내고 있지만, 가끔 팀원들과 안 맞는 디렉토리 구조, 페이지네이션을 위한 커스텀 훅 등등으로 부가시간이 많이 소요되고 있다. 커스텀훅을 제네릭으로 바꾸는 등 좀 더 리팩토링 하고 페이지를 더 빨리 생산해야 한다.

profile
개발당시에 직면한 이슈를 정리하는 곳

0개의 댓글