์์ํก ํ์ด์ง๋ฅผ ์ํด ๋ฌดํ์คํฌ๋กค์ ๊ตฌํํ๋ ๊ธฐ๋ก์ด๋ค.
๊ทผ๋ฐ ์ด์ ๋ฆฌ์กํธ์ฟผ๋ฆฌ์ useInfiniteQuery
๋ฅผ ๊ณ๋ค์ธ.
์ฒ์์ ๋จ์ํ๊ฒ state๋ฅผ ์ด์ฉํด์ ๋ฌดํ์คํฌ๋กค์ ๋ง๋๋ ๋ก์ง์ ์๊ฐํ๊ณ ์ ๊ทผํ๋ค๊ฐ ์ฝ์ง์ ์ข ํ๋ค.
setList(prev => [...prev, ...res.data.list]);
์ ๋ฐ์ดํฐ๋ฅผ ํจ์นญ์ ํ๊ฒ ๋๋ฉด, ๊ธฐ์กด์ ์๋ ๋ฐฐ์ด ์ํ์ ์๋ก ๋ฐ์์จ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ๋ค. ์คํ๋ ๋ ์ฐ์ฐ์ ๋๋ concat
์ ์ฌ์ฉํ ์ ์๋ค.
๊ทผ๋ฐ useInfiniteQuery๋ฅผ ์ฌ์ฉํ๋ฉด ์ด์ง์ฟต ๋ค๋ฆ.
1. useInfiniteQuery
const { data, fetchNextPage } = useInfiniteQuery<InfiniteTalkType, unknown>(
['talks'],
UsersApi.getTalks,
{
getNextPageParam: (lastPage) => lastPage.lastId,
},
);
์ฟผ๋ฆฌํจ์ ๋ถ๋ถ๋ง ๊ฐ์ ธ์ ๋ดค๋ค. ์ฟผ๋ฆฌ ํค์ ํจ์นญ ํจ์, ๊ทธ๋ฆฌ๊ณ ์ต์
์ ์ฐจ๋ก๋ก ๋ฃ์ด์ค๋ค. ์ ๋ค๋ฆญ์ <Data ํ์
, Error ํ์
>
์ ํด๋น๋๋ค. ์ ์ผ ์ค์ํ๊ฑด getNextPageParam
ํจ์๊ฐ ๋ค์ด๊ฐ๋ ์ต์
์ด๋ค. lastPage๋ผ๋ ์ด๋ฆ์ผ๋ก ์ธ์๋ก ๋ฐ์์ lastId๋ฅผ ๋ฐํํ๋ค.
lastPage๋ ๊ทธ๋ผ ๋ฌด์์ผ๊น(์ด๋ฆ์ lastPage๊ฐ ์๋ ๋ค๋ฅธ๊ฑฐ๋ผ๋ ์๊ด์๋ค). useInfiniteQuery
ํจ์๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ์ฌ๋ฌ๋ฒ์ ๊ฑธ์ณ ๊ฐ์ ธ์ฌ ๋ ๊ทธ ๋ฐ์ดํฐ๋ค์ด ์์์ ๋ฐฐ์ด ์ํ์ ์์ด๋๊ฒ ์๋๋ค. data ์์ pages์ ๋ฐฐ์ด๋ก ์ฐจ๊ณก์ฐจ๊ณก ์์ธ๋คโฝ¹โพ. ๊ทผ๋ฐ ๋ณดํต์ list๋ง ๊ฐ์ ธ์ค๋๊ฒ ์๋๋ผ, lastId๋ ๋ง์ง๋ง ๋ฐ์ดํฐ์ธ์ง ๋ฑ์ ์ ๋ณด๊ฐ ๊ฐ์ด ์ด.
์๋ฒ์์ ๋ฐ์์ค๋ ๋ฐ์ดํฐ์ ํ์์ ์์ ๊ฐ๋ค. ์์ํก ๋ฆฌ์คํธ๋ฅผ 20๊ฐ์ฉ ๋ฐ์์ค๊ณ , ๋ง์ง๋ง์ผ๋ก ๋ฐ์ ํก์ id์ ๋ง์ง๋ง ๋ฐ์ดํฐ ์ฌ๋ถ๋ฅผ ํจ๊ป ๋๊ฒจ์ค๋คโฝ²โพ
UsersApi.getTalks
interface InfiniteTalkType {
talkList: ITalk[];
lastId: number;
isLast: boolean;
};
...
...
getTalks: async ({ pageParam = null }): Promise<InfiniteTalkType> => {
const { data } = await axiosPrivate.get(
`users/comment?lastId=${pageParam}`,
);
return {
talkList: data.data.list,
lastId: data.data.meta.lastId,
isLast: data.data.meta.lastPage,
};
},
getNextPageParam
์ผ๋ก ๋ฃ์ด์ค lastId๊ฐ ์์๋ค. ์์ ์ฌ์ง 1์์ ๋ณผ ์ ์์. ๊ทธ ์ค์์ ๊ฐ์ฅ ๋ง์ง๋ง ์ธ๋ฑ์ค์ ๊ฐ์ด queryFn
์ ์ธ์๋ก ๋ค์ด๊ฐ๋ ๊ฒ. ๊ทธ ๊ฐ์ api url์ ์ฟผ๋ฆฌ์คํธ๋ง์ผ๋ก ๋ฃ์ด์ฃผ๊ณ ์์ฒญ์ ๋ณด๋ธ๋ค. ๊ทธ๋ผ ๊ทธ id์ ํด๋นํ๋ ์์ํก์ ๋ค์ id๋ถํฐ 20๊ฐ์ฉ ๋ ๋ฐ์์ฌ ์ ์๋ ๊ฒ. meta ๊ฐ์ฒด ์์ ์๋ ์ ๋ณด๋ค์ ๋ ํธํ๊ฒ ์ฐ๊ธฐ ์ํด ๊บผ๋ด์ ๊ฐ์ฒด ํ๋๋ก ๋ฆฌํดํ๋๋ก ํ๋ค.
<TalkListWrapper isOpen={isOpen} ref={talkListRef}>
{data?.pages.map((talkList) => (
<TalkList talkList={talkList.talkList} key={talkList.lastId} />
))}
<Observation />
</TalkListWrapper>;
..
..
const TalkList = ({ talkList }: { talkList: ITalk[] }) => {
return (
<Wrapper>
{talkList.map((talk) => (
<TalkBubble
nickName={talk.nickName}
content={talk.content}
createdAt={talk.createdAt}
iComment={talk.iComment}
key={talk.id}
/>
))}
</Wrapper>
);
};
export default TalkList;
๋ฌดํ์คํฌ๋กค์ ํตํด ์ฌ๋ฌ๋ฒ ํจ์นญํ ์๋ต์ ๋ฐ์ดํฐ๋ค์ pages ๋ฐฐ์ด์ ์ ์ฅ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ ๋ฐ์ดํฐ๋ค ๊ฐ๊ฐ์ list๊ฐ ์๊ณ , ๊ทธ list๋ฅผ ์์ํก ๋ฒ๋ธ๋ก ๊ทธ๋ ค์ฃผ์ด์ผ ํ๋ ๊ฒ. ์ปดํฌ๋ํธ๋ฅผ ํ๋ ๋ ์จ์ ๋ฐฐ์ด์ ๋๊ฐ ๋๋ ค์ฃผ์ด์ผํ๋ค. pages ๋ฐฐ์ด, ๊ทธ ์์ list ๋ฐฐ์ด.
2. Observer
์คํฌ๋กค์ ๋ด๋ฆด ๋, ํน์ ์์ ์ ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ผ ํ๋ค. react-intersection-observer
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ค. ์์ ์ฝ๋๋ธ๋ญ์์ <Observation />
์ด๋ผ๋ div ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ํด๋น div ์์๊ฐ ํ๋ฉด์ ๋ณด์ด๋ฉด ๋ค์ ๋ฐ์ดํฐ๋ฅผ ํจ์นญํ๋๋ก ํด๋ณด์.
useGetTalks.tsx
import { useInView } from 'react-intersection-observer';
const useGetTalks = () => {
const { data, fetchNextPage } = useInfiniteQuery<InfiniteTalkType, unknown>(
['talks'],
UsersApi.getTalks,
{
getNextPageParam: (lastPage) => lastPage.lastId,
},
);
const Observation = (): ReactElement => {
const [ref, inView] = useInView();
useEffect(() => {
if (!data) return;
const pageLastIdx = data.pages.length - 1;
const isLast = data?.pages[pageLastIdx].isLast;
if (!isLast && inView) fetchNextPage();
}, [inView]);
return <div className="observer" ref={ref} style={{ height: '20px' }} />;
};
return { data, Observation };
};
export default useGetTalks;
useGetTalks ์ปค์คํ
ํ
์ ๋ง๋ค์ด ์ฌ์ฉํ๋ค. fetchNextPage
๋ (getNextPageParam์ ํตํด ์ ์ฅ๋) pageParams์ ๋ง์ง๋ง ์ธ๋ฑ์ค ๊ฐ์ ์ฌ์ฉํด api ํจ์นญ ํจ์๋ฅผ ์คํํ๋ค.
์ฌ๊ธฐ์ ์ค์ํ๊ฒ ๋ณผ ๋ถ๋ถ์ div ์์๋ฅผ ๋ฐํํ๋ Observation ํจ์์ด๋ค. react-intersection-observer
์์ useInView๋ฅผ import ํด์จ๋ค. useInView์์ ์ ๊ณตํ๋ ref๋ฅผ div ์์์ ๋ฌ์ ์ฌ์ฉํ ์ ์๋ค. ํด๋น div๊ฐ ํ๋ฉด์ ๋ณด์ฌ์ง๊ฒ ๋๋ฉด inView ๋ถ๋ฆฌ์ธ ๊ฐ์ด true
๋ก ๋ฐ๋๋ค. ๊ฐ์ฅ ์ต๊ทผ์ ํจ์นญํ ๋ฐ์ดํฐ๊ฐ ๋ง์ง๋ง ๋ฐ์ดํฐ๊ฐ ์๋๊ณ , inView๊ฐ true
์ผ ๋, fetchNextPage()
ํจ์๋ฅผ ์คํํ๋ค.
๋งค 20๊ฐ item ์๋์ ๋์ด 20px ์ง๋ฆฌ Observation ๋ฐ์ค๊ฐ ๋ค์ด๊ฐ๋ค. ์คํฌ๋กค์ด ๊ฑฐ์ ์ตํ๋จ์ผ๋ก ๋ด๋ ค๊ฐ ๋ ์ฆ์์ ๋ค์ ๋ฐ์ดํฐ๊ฐ ๋ฐ๋ก๋ฐ๋ก ํจ์นญ๋๋ค.
์ด๋ ๊ฒ ReactQuery์ useInfiniteQuery
๋ฅผ ์ด์ฉํด์ ๋ฌดํ์คํฌ๋กค์ ๊ตฌํํด๋ณด์๋ค. ๊ฒฐ๊ตญ ์ค๋น์ ๋ค๋ฅธ ์ฌ๋์ ํ์ผ๋กใ