원티드 프리온보딩 FE 10월 챌린지 사전 미션으로 진행
(https://www.wanted.co.kr/events/pre_challenge_fe_26)
https://github.com/jonghoon7431/wanted-pre-onboarding-fe-26
GitHub - jonghoon7431/wanted-pre-onboarding-fe-26: 원티드 프리온보딩 FE 챌린지 10월 (2024) 사전 미션(무한스
원티드 프리온보딩 FE 챌린지 10월 (2024) 사전 미션(무한스크롤). Contribute to jonghoon7431/wanted-pre-onboarding-fe-26 development by creating an account on GitHub.
github.com
제공된 mockdata
import { MockData } from "../types/mockdataType";
export const MOCK_DATA: MockData[] = [
{
productId: "66e1c1df3594bb65169e0f9b",
productName: "Elegant Granite Fish",
price: 792.0,
boughtDate: "Sat Jun 01 1985 20:00:06 GMT+0900 (한국 표준시)",
},
{
productId: "66e1c1df3594bb65169e0f9c",
productName: "Intelligent Steel Towels",
price: 287.0,
boughtDate: "Sun Jul 02 2017 01:41:40 GMT+0900 (한국 표준시)",
},
//...
]
제공된 호출용 promise 함수
const PER_PAGE = 10;
// 페이지는 1부터 시작함
const getMockData = (pageNum: number) => {
return new Promise((resolve) => {
setTimeout(() => {
const datas: MockData[] = MOCK_DATA.slice(
PER_PAGE * pageNum,
PER_PAGE * (pageNum + 1)
);
const isEnd = PER_PAGE * (pageNum + 1) >= MOCK_DATA.length;
resolve({ datas, isEnd });
}, 1500);
});
};
1. useIntersectionObserver 훅 생성
import { useCallback, useRef } from "react";
export default function useIntersectionObserver(callback: () => void) {
const observer = useRef<IntersectionObserver | null>(null);
const observe = useCallback(
(el: Element) => {
if (observer.current) observer.current.disconnect();
observer.current = new IntersectionObserver( //IntersectionObserver 생성
(entries) => {
console.log(entries);
if (entries[0].isIntersecting) {
//대상 요소가 관찰자 루트와 교차할 경우
callback(); //callback fn 실행
}
},
{ threshold: 0.1 }
);
if (el) observer.current.observe(el);
},
[callback]
);
const unobserve = useCallback((el: any) => {
if (observer.current) {
observer.current.unobserve(el);
}
}, []);
return [observe, unobserve] as const;
}
2. ProductList.tsx 파일 작성
import { useCallback, useEffect, useRef, useState } from "react";
import useIntersectionObserver from "../hooks/useIntersectionObserver";
import { MOCK_DATA } from "../mockdata/mockdata";
import { MockData } from "../types/mockdataType";
const ProductList = () => {
//2-1. data 담을 state, 로딩 여부 및 데이터가 더 존재하는지 담을 useState생성
const [data, setData] = useState<MockData[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const PER_PAGE = 10;
const page = useRef(0);
// 페이지는 1부터 시작함
const getMockData = (pageNum: number) => {
return new Promise((resolve) => {
setTimeout(() => {
const datas: MockData[] = MOCK_DATA.slice(
PER_PAGE * pageNum,
PER_PAGE * (pageNum + 1)
);
const isEnd = PER_PAGE * (pageNum + 1) >= MOCK_DATA.length;
resolve({ datas, isEnd });
}, 1500);
});
};
//ref 선언
const loadMoreRef = useRef(null);
//useIntersectionObserver에 넘겨줄 콜백함수
const loadMore = useCallback(() => {
if (isLoading || !hasMore) return;
setIsLoading(true);
getMockData(page.current).then((result: any) => {
setIsLoading(false);
setData((prev) => [...prev, ...result.datas]);
page.current += 1;
setHasMore(!result.isEnd);
});
}, [page, isLoading, hasMore]);
const [observe, unobserve] = useIntersectionObserver(loadMore);
useEffect(() => {
const currentRef = loadMoreRef.current;
if (currentRef) {
observe(currentRef);
}
return () => {
if (currentRef) {
unobserve(currentRef);
}
};
}, [observe, unobserve]);
//총액 계산
const totalPrice = data.reduce((acc, data) => acc + data.price, 0);
return (
<main>
<h1 className="flex justify-center text-[32px] my-6">
프리온보딩 FE 챌린지 10월 (2024) 리액트 오픈소스 펼쳐보기 사전 미션
</h1>
<ul className="flex flex-col gap-4 items-center">
<p>가격 총액: {totalPrice}</p>
{data.map((data) => (
<li key={data.productId} className="border-2 p-2 text-[20px]">
<p>id: {data.productId}</p>
<p>name : {data.productName}</p>
<p>price : {data.price}</p>
<p>bought date : {data.boughtDate}</p>
</li>
))}
</ul>
{isLoading && (
<div className="flex justify-center text-[24px] m-4">Loading</div>
)}
{!isLoading && hasMore && (
<div className="flex justify-center text-[24px] m-4" ref={loadMoreRef}>
Loading
</div>
)}
{!hasMore && (
<div className="flex justify-center text-[24px] m-4">
No more products to load
</div>
)}
</main>
);
};
export default ProductList;
'today,weekly I learn' 카테고리의 다른 글
중요 요청 체이닝 (0) | 2024.11.26 |
---|---|
zustand 타입스크립트에서 사용하기(기본사용법) (0) | 2024.11.19 |
최종 프로젝트 간단한 리팩토링 (1) | 2024.10.16 |
리뷰 기능 수정 (2) | 2024.09.30 |
React fragment tag <> (2) | 2024.09.25 |