일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- app router
- CORS
- SSR
- 호이스팅
- 공변성
- 인터섹션
- 결정 알고리즘
- recoil
- map
- 이분 검색
- dfs
- ESlint
- 타입 좁히기
- 무한 스크롤
- async/await
- React
- Jest
- 태그된 유니온
- Cypress
- 리터럴 타입
- useAppDispatch
- TS
- CI/CD
- tailwind
- autosize
- RTK Query
- 반공변성
- webpack
- 투포인터
- Promise
Archives
- Today
- Total
짧은코딩
전체 상품 불러오기 무한 스크롤 구현 with react-intersection-observer 본문
반응형
구현 화면 및 사용 기술
fakejs와 MSW를 이용하여 mock API를 만들어서 구현했다.
"react-intersection-observer"를 이용하여 총 아이템 중 마지막에서 4번째 위치에 있는 아이템을 사용자가 보면 데이터를 더 불러오도록 했다.
(다음엔 serializeQueryArgs, merge, forceRefetch도 같이 이용하여 구현해보려 한다..!)
구현
RTK Query
우선 RTK Query의 endpoints에 위와 같이 코드를 작성했다.
fakejs
백엔드와 위 데이터 형식으로 소통하기로 했기에 fakejs를 통해 위 데이터 구조를 만들었다.
import { faker } from "@faker-js/faker";
export function createRandomItem() {
return {
itemId: faker.number.int(),
itemName: faker.commerce.product(),
shopName: faker.company.name(),
basicPrice: faker.commerce.price(),
imageUrl: faker.image.url(),
category: faker.commerce.productAdjective(), // userId: faker.string.uuid(),
};
}
각 아이템에 따른 구조이다.
MSW
import { rest } from "msw";
import { createRandomItem } from "@mock/api/data/items/getItems";
import { faker } from "@faker-js/faker";
export const getItems = [
rest.get("/items", async (req, res, ctx) => {
const id = req.url.searchParams.get("page")!;
const tempItems = faker.helpers.multiple(createRandomItem, { count: 20 });
const items = {
items: tempItems,
finalPage: +id === 10,
};
return res(ctx.status(200), ctx.json(items));
}),
];
MSW에서는 위에서 fakejs로 구현한 아이템을 20개씩 보내주도록 했다.
finalPage는 10번째 페이지 요청이 오면 true를 주도록 했다.
바닥 감지하기(react-intersection-observer)
-전체 상품을 보여주는 컴포넌트(MainItem/index.tsx)
const MainItem = () => {
const [page, setPage] = useState(1);
const { data, error, isLoading } = itemsApi.useGetAllItemsQuery(page);
const [items, setItems] = useState<item[]>([]);
const [finalPage, setFinalPage] = useState(false);
const { ref, inView } = useInView();
useEffect(() => {
if (inView && !finalPage) {
setPage((prev) => prev + 1);
}
}, [inView, finalPage]);
useEffect(() => {
if (data) {
setItems((prev) => [...prev, ...data.items]);
setFinalPage(data.finalPage);
}
}, [data]);
return (
<Wrapper>
<TitleContainer>
<h2>전체 상품</h2>
</TitleContainer>
{error && <div>새로고침하여 주세요.</div>}
{isLoading && <div>로딩중...</div>}
{data && (
<ItemContainer>
{items?.map((item: item, index) => {
return (
<Link
to={`/eachitem/${item.itemId}`}
key={item.itemId}
ref={items.length - 5 === index ? ref : null}
>
<ItemBox>
<ItemImg>
<img src={item.imageUrl} alt={"상품 사진"} />
</ItemImg>
<ItemInfo>
<ItemName>{item.itemName}</ItemName>
<span>{item.category}</span>
<ItemPrice>{item.basicPrice}원</ItemPrice>
</ItemInfo>
</ItemBox>
</Link>
);
})}
</ItemContainer>
)}
</Wrapper>
);
};
- 변수들
- page를 이용하여 다음 페이지의 상품들을 가져왔다.
- {data, error, isLoading}을 이용해 데이터를 가져오고, 에러 처리, 데이터를 가져오는 중을 적용시켰다.
- items는 가져온 data들을 모아서 보여주는 역할을 했다.
- finalPage는 마지막 페이지인 경우 값을 저장하는 용도로 사용했다.
- {ref, inView}을 사용해서 내가 지정한 상품을 보고 있는지 여부를 체크알 수 있다.
- useEffect
- 만약 inView가 true여서 상품을 보고 있고 finalPage가 아니면 page를 +1 해서 데이터를 더 불러왔다.
- 데이터를 가져왔으면 items에 가져온 데이터들을 추가하고 finalPage에 마지막 페이지인지를 기록했다.
- 화면
- error가 있으면 새로고침하라는 메시지가 나오도록 했다.
- isLoading이면 로딩 중이란 메시지가 나오도록 했다.
- data가 있으면 아이템들을 보여줬다.
- items에서 마지막 4번째 아이템을 보면 데이터를 더 불러옴
구현 완료
이렇게 구현을 완료했다!
(일단은 10번째 페이지가 마지막 페이지로 설정해서 구현했다.)
반응형
'wayc 이커머스 프로젝트' 카테고리의 다른 글
스크롤 맨 위로 올리기 with react-custom-scrollbars (1) | 2024.01.07 |
---|---|
MSW1 사용 중 Warning: captured a request without a matching request handler (2) | 2024.01.03 |
Webpack의 HMR과 자동 새로고침 현상 (0) | 2023.11.12 |
로그인 전략 with Cookie (0) | 2023.10.24 |
MSW 적용하기 (0) | 2023.09.20 |
Comments