๐ค ์์งlog ๐ค
[REACT] ๋ฌดํ ์คํฌ๋กค (Infinite Scroll) ๊ตฌํํ๊ธฐ ๋ณธ๋ฌธ
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ง ์๊ณ { useState, useCallback, useEffect, useRef } ๋ฅผ ์ฌ์ฉํด์ ์ง์ ๊ตฌํํด๋ณด๊ธฐ๋ก ํ์๋ค ! ๐ฅ
์ฌ์ฉํ๊ธฐ๋ง ํด๋ดค์ง ์ง์ ๊ตฌํํ๋๋ฐ์๋ ๋๋ฌด ์ด๋ ต๊ณ ์ดํด๋ ์ ์๋์์ง๋ง
์ ๋ณด๋ ๋ง์๊ณ , ๋์๋ ๋ฐ์๊ฐ๋ฉฐ ์ด๊ฒ ์ ๊ฒ ์๋ก ์๋ํด๋ณด๋ ์ฌ๋ฏธ๋์์๋ค -๐
๐ ๊ตฌํ ์ฝ๋
import { useState, useCallback, useEffect, useRef } from "react";
import styled from "styled-components";
import { getPostList, postType } from "datas/postList";
const List = (): JSX.Element => {
const [page, setPage] = useState<number>(1);
const [posts, setPosts] = useState<postType[]>(getPostList(1));
const ref = useRef<any>(null);
const handleScroll = useCallback((): void => {
const { clientHeight, scrollTop, scrollHeight } = ref.current;
// ์คํฌ๋กค์ ํ๋ฉด์ ์คํํ ๋ด์ฉ์ ์ด๊ณณ์ ์ถ๊ฐํฉ๋๋ค.
if (clientHeight + scrollTop >= scrollHeight - 1) {
// clientHeight ๊ณผ scrollTop๋ฅผ ๋ํ ๊ฐ์ด scrollHeight - 1 ๋ณด๋ค ํฌ๋ค๋ฉด, ๊ฐ์ฅ ์๋์ ๋๋ฌํ๋ค๋ ์๋ฏธ์
๋๋ค.
setPosts(posts.concat(getPostList(page + 1)));
// ํ์ด์ง์ ๋ฐ๋ผ์ ๋ถ๋ฌ์จ ๋ฐฐ์ด์ posts ๋ฐฐ์ด๊ณผ ํฉ์ณ์ค๋๋ค.
setPage((prevPage: number) => prevPage + 1);
// ํ์ด์ง state ๋ณ์์ ๊ฐ๋ 1์ฉ ๋๋ ค์ค๋๋ค.
}
}, [page, posts]);
useEffect(() => {
window.addEventListener("scroll", handleScroll, true);
// ์คํฌ๋กค์ด ๋ฐ์ํ ๋๋ง๋ค handleScroll ํจ์๋ฅผ ํธ์ถํ๋๋ก ์ถ๊ฐํฉ๋๋ค.
return () => {
window.removeEventListener("scroll", handleScroll, true);
// ํด๋น ์ปดํฌ๋ํธ๊ฐ ์ธ๋ง์ดํธ ๋ ๋, ์คํฌ๋กค ์ด๋ฒคํธ๋ฅผ ์ ๊ฑฐํฉ๋๋ค.
};
}, [handleScroll]);
return (
<Container>
<Content ref={ref}>
{posts.map(({ id, img, contents }: postType) => (
<Box key={id}>
<img src={img} alt="" />
<Text>{contents}</Text>
</Box>
))}
</Content>
</Container>
);
};
const Container = styled.div`
overflow: hidden;
display: flex;
flex-direction: column;
align-items: flex-start;
background-color: #ebebeb;
padding-bottom: 40px;
`;
const Content = styled.div`
max-height: 500px;
overflow: auto;
`;
const Box = styled.div`
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
img {
border-radius: 5px;
position: relative;
left: 10px;
}
`;
const Text = styled.div`
width: 295px;
height: 45px;
margin: 10px 0;
background: #ffffff;
border-radius: 5px;
font-size: 14px;
font-weight: 600;
padding: 0 30px;
display: flex;
align-items: center;
`;
export default List;
export type postType = {
id: number;
page: number;
contents: string;
img: string;
};
export const getPostList = (page: number): postType[] => {
return postList.filter((post: postType) => post.page === page);
// ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ ํ์ด์ง์ ๋์ผํ ํ์ด์ง ๊ฐ์ฒด๋ค๋ง return ํด์ค๋๋ค.
};
export const postList: postType[] = [
{
id: 0,
page: 1,
img: "http://via.placeholder.com/60x45",
contents: "์๋
ํ์ธ์ 1๋ฒ์งธ ๊ธ",
},
{
id: 1,
page: 1,
img: "http://via.placeholder.com/60x45",
contents: "์๋
ํ์ธ์ 2๋ฒ์งธ ๊ธ",
},
{
id: 2,
page: 1,
img: "http://via.placeholder.com/60x45",
contents: "์๋
ํ์ธ์ 3๋ฒ์งธ ๊ธ",
},
{
id: 3,
page: 1,
img: "http://via.placeholder.com/60x45",
contents: "์๋
ํ์ธ์ 4๋ฒ์งธ ๊ธ",
},
{
id: 4,
page: 1,
img: "http://via.placeholder.com/60x45",
contents: "์๋
ํ์ธ์ 5๋ฒ์งธ ๊ธ",
},
{
id: 5,
page: 1,
img: "http://via.placeholder.com/60x45",
contents: "์๋
ํ์ธ์ 6๋ฒ์งธ ๊ธ",
},
{
id: 6,
page: 1,
img: "http://via.placeholder.com/60x45",
contents: "์๋
ํ์ธ์ 7๋ฒ์งธ ๊ธ",
},
{
id: 7,
page: 1,
img: "http://via.placeholder.com/60x45",
contents: "์๋
ํ์ธ์ 8๋ฒ์งธ ๊ธ",
},
{
id: 8,
page: 1,
img: "http://via.placeholder.com/60x45",
contents: "์๋
ํ์ธ์ 9๋ฒ์งธ ๊ธ",
},
{
id: 9,
page: 1,
img: "http://via.placeholder.com/60x45",
contents: "์๋
ํ์ธ์ 10๋ฒ์งธ ๊ธ",
},
// ...
]
๊ทธ๋๋ ๊ฒฐ๊ณผ ๋ณด๋ฉด ๋ฟ๋ฏ -โค๏ธ
728x90
'๐ React' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[REACT] No QueryClient set, use QueryClientProvider to set one(error) (0) | 2023.08.24 |
---|---|
[REACT] useQuery ์ useMutation (0) | 2023.03.30 |
[REACT] CSS ๋ฐ์ํ ๋ ์ด์์ ๋๋น๋ฅผ ๋๋๋ ๊ธฐ์ค ์ ํ๊ธฐ (0) | 2023.02.10 |
[REACT] Carousel๋ก ๋ฐฐ๋ ์ฌ๋ผ์ด๋ ๊ตฌํํ๊ธฐ (0) | 2023.02.06 |
[REACT] Swiper ์ฌ์ฉํ์ฌ ํฐ์น์ฌ๋ผ์ด๋ ๊ตฌํํ๊ธฐ (1) | 2023.02.06 |