Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ui-ux): fix multiple re-renders, add common useFetchListData hook #303

Merged
merged 4 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/components/TransactionDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import PaginationLoader from "@components/skeletonLoaders/PaginationLoader";
import { RawTransactionI, TxnNextPageParamsProps } from "@api/types";
import { transformTransactionData } from "shared/transactionDataHelper";
import { PaginationSource } from "enum/tabsTitle";

interface TransactionsProps {
transactions: RawTransactionI[];
Expand All @@ -29,6 +30,9 @@ function TxnPagination({
pathname: string;
nextPageParams?: TxnNextPageParamsProps;
}) {
const source = pathname.includes("/txs")
? undefined // source not needed for main transaction list page (/txs)
: PaginationSource.Transactions;
return (
<Pagination<TxnQueryParamsProps>
pathname={pathname}
Expand All @@ -41,6 +45,7 @@ function TxnPagination({
}
: undefined
}
source={source}
/>
);
}
Expand Down
14 changes: 14 additions & 0 deletions src/components/commons/Pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,21 @@ interface PaginationProps<T> {
pathname?: string;
containerClass?: string;
shallow?: boolean;
source?: string;
}

export default function Pagination<T>({
nextPageParams: nextPageParamsFromApi,
pathname,
containerClass,
shallow,
source,
}: PaginationProps<T>): JSX.Element {
const router = useRouter();
const pathName = pathname ?? router.pathname;
const currentPageNumber = Number(router.query.page_number ?? 1);
const nextPageParams = {
...(source !== undefined && { source }),
...nextPageParamsFromApi,
...{ page_number: currentPageNumber + 1 },
};
Expand Down Expand Up @@ -66,6 +69,17 @@ export default function Pagination<T>({
return [pageButton.previous, pageButton.current, pageButton.next];
};

useEffect(() => {
// Set `source` params on page load
// Update `source` params on tab change
if (
(source !== undefined && router.query.source === undefined) ||
source !== router.query.source
) {
router.query.source = source;
}
}, [source]);

useEffect(() => {
if (
!previousPagesParams.some(
Expand Down
9 changes: 9 additions & 0 deletions src/enum/tabsTitle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,12 @@ export enum AddressContractTabsTitle {
TokenTransfers = "Token Transfers",
TokenHolders = "Token Holders",
}

export enum PaginationSource {
Transactions = "transactions",
Contract = "contract",
ContractTokens = "contract-tokens",
Logs = "logs",
TokenTransfers = "token-transfers",
TokenHolders = "token-holders",
}
45 changes: 45 additions & 0 deletions src/hooks/useFetchListData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import { sleep } from "shared/sleep";
import { PaginationSource } from "enum/tabsTitle";

interface FetchDataParams {
addressHash: string;
triggerApiCall: () => any;
fullstackninja864 marked this conversation as resolved.
Show resolved Hide resolved
source: PaginationSource;
}

interface FetchDataResponse<T, S> {
isLoading: boolean;
data: T[];
nextPage: S | undefined;
}

export default function useFetchListData<T, S>({
addressHash,
triggerApiCall,
source,
}: FetchDataParams): FetchDataResponse<T, S> {
const [isLoading, setIsLoading] = useState(true);
const [data, setData] = useState<T[]>([]);
const [nextPage, setNextPage] = useState<S>();
const router = useRouter();

const fetchData = async () => {
setIsLoading(true);
const resp = await triggerApiCall().unwrap();
setData(resp.items ?? []);
setNextPage(resp.next_page_params);
await sleep(150); // added timeout to prevent flicker
setIsLoading(false);
};

useEffect(() => {
const skipDataFetch = router.query.source !== source;
if (!skipDataFetch) {
fetchData();
}
}, [router.query.page_number, addressHash]);

return { isLoading, data, nextPage };
}
38 changes: 17 additions & 21 deletions src/pages/address/_components/ContractTokensList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import clsx from "clsx";
import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import { useNetwork } from "@contexts/NetworkContext";
import { TokenItemI, TokensListPageParamsProps } from "@api/types";
Expand All @@ -10,7 +9,8 @@ import {
SkeletonLoaderScreen,
} from "@components/skeletonLoaders/SkeletonLoader";
import { useGetContractTokensMutation } from "@store/token";
import { sleep } from "shared/sleep";
import useFetchListData from "@hooks/useFetchListData";
import { PaginationSource } from "enum/tabsTitle";
import DetailRowTitle from "./DetailRowTitle";
import ContractTokenRow, { TokenTableFixedTitle } from "./ContractTokenRow";

Expand All @@ -20,29 +20,24 @@ interface TokenDetailsProps {

export default function ContractTokensList({ addressHash }: TokenDetailsProps) {
const { connection } = useNetwork();
const [tokens, setTokens] = useState<TokenItemI[]>([]);
const [nextPage, setNextPage] = useState<TokensListPageParamsProps>();
const [isLoading, setIsLoading] = useState(true);
const [trigger] = useGetContractTokensMutation();
const router = useRouter();

const params = router.query;
const fetchTokens = async () => {
setIsLoading(true);
const tokenList = await trigger({
network: connection,
addressHash,
queryParams: params,
}).unwrap();
setTokens(tokenList.items);
setNextPage(tokenList.next_page_params as TokensListPageParamsProps);
await sleep(150); // added timeout to prevent flicker
setIsLoading(false);
};

useEffect(() => {
fetchTokens();
}, [params.page_number, addressHash]);
const {
data: tokens,
isLoading,
nextPage,
} = useFetchListData<TokenItemI, TokensListPageParamsProps>({
addressHash,
source: PaginationSource.ContractTokens,
triggerApiCall: () =>
trigger({
network: connection,
addressHash,
queryParams: params,
}),
});

if (!isLoading && tokens.length === 0) {
return <div className="text-white-50">No contract tokens</div>;
Expand Down Expand Up @@ -129,6 +124,7 @@ function TokensListPagination({
<Pagination<TokensListPageParamsProps>
pathname={`/address/${addressHash}`}
nextPageParams={nextPageParams}
source={PaginationSource.ContractTokens}
shallow
/>
</div>
Expand Down
43 changes: 20 additions & 23 deletions src/pages/address/_components/LogsList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Fragment, useEffect, useState } from "react";
import { Fragment } from "react";
import { useRouter } from "next/router";
import {
useGetAddressLogsMutation,
Expand All @@ -13,35 +13,31 @@ import {
SkeletonLoader,
SkeletonLoaderScreen,
} from "@components/skeletonLoaders/SkeletonLoader";
import { sleep } from "shared/sleep";
import useFetchListData from "@hooks/useFetchListData";
import { PaginationSource } from "enum/tabsTitle";

export default function LogsList({ addressHash }: { addressHash: string }) {
const { connection } = useNetwork();
const [logs, setLogs] = useState<Log[]>([]);
const [nextPage, setNextPage] = useState<LogsPageParamsProps>();
const [isLoading, setIsLoading] = useState(true);
const [trigger] = useGetAddressLogsMutation();
const router = useRouter();

const params = router.query;
const fetchLogs = async () => {
setIsLoading(true);
const data = await trigger({
network: connection,
itemsCount: params.items_count as string,
blockNumber: params.block_number as string,
index: params.index as string,
addressHash,
}).unwrap();
setLogs(data.items);
setNextPage(data.next_page_params);
await sleep(150); // added timeout to prevent flicker
setIsLoading(false);
};

useEffect(() => {
fetchLogs();
}, [params.page_number, addressHash]);
const {
data: logs,
isLoading,
nextPage,
} = useFetchListData<Log, LogsPageParamsProps>({
addressHash,
source: PaginationSource.Logs,
triggerApiCall: () =>
trigger({
network: connection,
itemsCount: params.items_count as string,
blockNumber: params.block_number as string,
index: params.index as string,
addressHash,
}),
});

if (!isLoading && logs.length === 0) {
return <div className="text-white-50">No logs</div>;
Expand Down Expand Up @@ -146,6 +142,7 @@ function LogsPagination({
}
: undefined
}
source={PaginationSource.Logs}
shallow
/>
</div>
Expand Down
40 changes: 18 additions & 22 deletions src/pages/address/_components/TokenHoldersList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import BigNumber from "bignumber.js";
import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import { useNetwork } from "@contexts/NetworkContext";
import {
Expand All @@ -16,40 +15,36 @@ import Pagination from "@components/commons/Pagination";
import LinkText from "@components/commons/LinkText";
import { truncateTextFromMiddle } from "shared/textHelper";
import NumericFormat from "@components/commons/NumericFormat";
import { sleep } from "shared/sleep";
import { GWEI_DECIMAL } from "shared/constants";
import { formatUnits } from "viem";
import useFetchListData from "@hooks/useFetchListData";
import { PaginationSource } from "enum/tabsTitle";

export default function TokenHoldersList({
addressHash,
}: {
addressHash: string;
}) {
const { connection } = useNetwork();
const [holders, setHolders] = useState<TokenHolderProps[]>([]);
const [nextPage, setNextPage] = useState<TokenHolderPageParamsProps>();
const [isLoading, setIsLoading] = useState(true);
const [trigger] = useGetTokenHoldersMutation();
const router = useRouter();

const params = router.query;
const fetchTokenHolders = async () => {
setIsLoading(true);
const data = await trigger({
network: connection,
tokenId: addressHash,
itemsCount: params.items_count as string,
value: params.value ? BigInt(Number(params.value)).toString() : "",
}).unwrap();
setHolders(data.items);
setNextPage(data.next_page_params);
await sleep(150); // added timeout to prevent flicker
setIsLoading(false);
};

useEffect(() => {
fetchTokenHolders();
}, [params.page_number, addressHash]);
const {
data: holders,
isLoading,
nextPage,
} = useFetchListData<TokenHolderProps, TokenHolderPageParamsProps>({
addressHash,
source: PaginationSource.TokenHolders,
triggerApiCall: () =>
trigger({
network: connection,
tokenId: addressHash,
itemsCount: params.items_count as string,
value: params.value ? BigInt(Number(params.value)).toString() : "",
}),
});

if (!isLoading && holders.length === 0) {
return <div className="text-white-50">No token holders</div>;
Expand Down Expand Up @@ -117,6 +112,7 @@ function HoldersPagination({
}
: undefined
}
source={PaginationSource.TokenHolders}
shallow
/>
</div>
Expand Down
Loading
Loading