Skip to content

Commit

Permalink
Merge pull request #180 from Kernel360/feature-homepage-infinite-scroll
Browse files Browse the repository at this point in the history
페이지 기능: 홈페이지 무한 스크롤 구현
  • Loading branch information
bottlewook authored Feb 20, 2024
2 parents e235bda + d7435e6 commit 0d43977
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 20 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"react-cookie": "^7.0.2",
"react-dom": "^18",
"react-hook-form": "^7.49.2",
"react-infinite-scroll-component": "^6.1.0",
"react-redux": "^9.0.4",
"react-slick": "^0.29.0",
"redux-persist": "^6.0.0"
Expand Down
30 changes: 22 additions & 8 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
/* eslint-disable react/no-array-index-key */
/* eslint-disable @typescript-eslint/no-unused-vars */

'use client';

import InfiniteScroll from 'react-infinite-scroll-component';

import classNames from 'classnames/bind';

import { MOCK_BANNER_DATA, MOCK_PRODUCT_LIST, MOCK_RECOMMEND_PRODUCTS } from '@mocks/homeHandler/mocks';
import { MOCK_BANNER_DATA, MOCK_RECOMMEND_PRODUCTS } from '@mocks/homeHandler/mocks';
import useBanner from '@remote/queries/home/useBanner';
import useProductList from '@remote/queries/home/useProductList';
import useRecommendProducts from '@remote/queries/home/useRecommendProducts';
Expand Down Expand Up @@ -38,6 +41,10 @@ function Home() {
// return <Loader />;
// }

const {
data: productList, isLoading, hasNextPage, loadMore, isFetching,
} = useProductList();

return (
<>
<Header className={cx('home')} type="home" />
Expand All @@ -52,7 +59,7 @@ function Home() {
</div>
<Spacing size={35} />
<div className={cx('productListWrapper')}>
<Text typography="t4" bold>WashPedia 랭킹</Text>
<Text typography="t4" bold>WashFit 랭킹</Text>
<Spacing size={16} />
<Flex justify="space-between" align="center" gap={8}>
<Radio label="조회순" name="filter" type="filter" value="view" defaultChecked />
Expand All @@ -61,12 +68,19 @@ function Home() {
<Radio label="최신순" name="filter" type="filter" value="latest" />
</Flex>
<Spacing size={16} />
<div className={cx('productArticleWrapper')}>
{MOCK_PRODUCT_LIST?.value.map((item) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
return <ProductArticle key={item.productNo} itemData={item} />;
})}
</div>
<InfiniteScroll
dataLength={productList?.length ?? 0}
next={loadMore}
hasMore={hasNextPage}
loader={<div className="loader" key={0}>Loading ...</div>}
inverse={false}
>
<div className={cx('productArticleWrapper')}>
{productList?.map((item, index) => {
return <ProductArticle key={index} itemData={item} />;
})}
</div>
</InfiniteScroll>
</div>
<ScrollToTop />
</main>
Expand Down
2 changes: 1 addition & 1 deletion src/components/shared/product-article/ProductArticle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function ProductArticle({ isRow = false, itemData }: ProductArticleProps) {
<article className={cx('container', { row: isRow })}>
<div className={cx('imgBox')}>
<Image
src={itemData.imageSource!}
src={itemData.imageSource ?? '/assets/profile.JPG'}
alt="제품 이미지"
width={0}
height={0}
Expand Down
6 changes: 3 additions & 3 deletions src/remote/api/requests/home/home.get.api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
BannerType, ProductType, RecommendProductsType,
BannerType, ProductListInfoType, RecommendProductsType,
} from '../../types/home';
import { getRequest } from '../requests.api';

Expand All @@ -18,8 +18,8 @@ export const getRecommendProducts = async () => {
};

/* ----- 제품 목록 정보 api ----- */
export const getProductList = async () => {
const response = await getRequest<ProductType>('/products?sortType=viewcnt_order');
export const getProductList = async (pageNum: number, size: number) => {
const response = await getRequest<ProductListInfoType>(`/products/rank?sortType=recent-order&page=${pageNum}&size=${size}`);

return response;
};
42 changes: 37 additions & 5 deletions src/remote/api/types/home.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,53 @@ export interface IRecommendProducts extends IBanner {
}

export interface IProduct {
brand: string
upperItem: string
productNo: number
imageSource: string
productName: string
safetyStatus: string
barcode: string
imageSource: string
reportNumber: string
safetyStatus: string
viewCount: number
brand: string
upperItem: string
createdAt: string
createdBy: string
modifiedAt: string
modifiedBy: string
}

export interface IProductContent {
content: IProduct[]
}

export interface IProductPageInfo {
pageable: {
pageNumber: number
pageSize: number
sort: {
empty: boolean
sorted: boolean
unsorted: boolean
}
offset: number
paged: boolean
unpaged: boolean
}
totalPages: number
totalElements: number
last: boolean
size: number
number: number
sort: {
empty: boolean
sorted: boolean
unsorted: boolean
}
numberOfElements: number
first: boolean
empty: boolean
}

export interface IProductDetails extends IProduct {
companyName: string
productType: string
Expand All @@ -44,5 +76,5 @@ export interface IProductDetails extends IProduct {

export type BannerType = ICommon<IBanner[]>;
export type RecommendProductsType = ICommon<IRecommendProducts[]>;
export type ProductType = ICommon<IProduct[]>;
export type ProductListInfoType = ICommon<IProductContent & IProductPageInfo>;
export type ProductDetailsType = ICommon<IProductDetails>;
34 changes: 31 additions & 3 deletions src/remote/queries/home/useProductList.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,38 @@
import { useQuery } from '@tanstack/react-query';
import { useCallback } from 'react';

import { useInfiniteQuery } from '@tanstack/react-query';

import { getProductList } from '@remote/api/requests/home/home.get.api';
import { ProductType } from '@remote/api/types/home';

function useProductList() {
return useQuery<ProductType>({ queryKey: ['productList'], queryFn: getProductList });
const {
data: productList, isLoading, fetchNextPage, isFetching, hasNextPage = false,
} = useInfiniteQuery({
queryKey: ['productList'],
queryFn: ({ pageParam }) => { return getProductList(pageParam as number, 10); },
getNextPageParam: (lastPage) => {
return (
lastPage.value.last
? undefined
: lastPage.value.pageable.pageNumber + 1
);
},
suspense: true,
});

const loadMore = useCallback(async () => {
if (hasNextPage === false || isFetching) {
return;
}

await fetchNextPage();
}, [fetchNextPage, hasNextPage, isFetching]);

const data = productList?.pages.map((page) => { return page.value.content; }).flat();

return {
data, isLoading, loadMore, isFetching, hasNextPage,
};
}

export default useProductList;
12 changes: 12 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9547,6 +9547,13 @@ react-hook-form@^7.49.2:
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.49.2.tgz#6fb2742e1308020f26cb1915c7012b6c07b11ade"
integrity sha512-TZcnSc17+LPPVpMRIDNVITY6w20deMdNi6iehTFLV1x8SqThXGwu93HjlUVU09pzFgZH7qZOvLMM7UYf2ShAHA==

react-infinite-scroll-component@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz#7e511e7aa0f728ac3e51f64a38a6079ac522407f"
integrity sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ==
dependencies:
throttle-debounce "^2.1.0"

[email protected]:
version "18.1.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.1.0.tgz#61aaed3096d30eacf2a2127118b5b41387d32a67"
Expand Down Expand Up @@ -10907,6 +10914,11 @@ text-table@^0.2.0:
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==

throttle-debounce@^2.1.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-2.3.0.tgz#fd31865e66502071e411817e241465b3e9c372e2"
integrity sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==

through2@^2.0.3:
version "2.0.5"
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
Expand Down

0 comments on commit 0d43977

Please sign in to comment.