-
10일 차 (무한 스크롤 구현)project/main 2023. 3. 16. 20:48
CSS가 어느정도 끝났기 때문에 슬슬 필요한 기능구현을 하기로 했다. 그 중에서 나는 무한 스크롤을 하기로 했다.
1. 무한 스크롤(Infinite Scroll)
리스트들을 보는 방법은 여러가지가 있다.
- 데이터를 전부 받아와서 전부 한번에 나타내기
- 페이지로 나누어 보기
- 무한 스크롤
이 중 내가 구현하려는 무한 스크롤은 데이터의 일부를 받아오고 이후 스크롤이 정해진 구간을 넘어 갔을때 다시 요청을 보내서 스크롤이 늘어나는 방식으로 리스트를 보여주는 방식을 말한다.
구현하는 방식은 얼추 알고 있었다.
- 일정 부분의 데이터를 처음받고 map으로 뿌려준다.
- 스크롤이 content의 마지막에 도달했을때 다시 데이터 요청으로 보내고 리스트 업데이트 하기.
import { useState, useEffect } from 'react'; import styled from 'styled-components'; import axios from 'axios'; const Container = styled.div` display: flex; flex-wrap: wrap; `; const Item = styled.div` width: 200px; height: 200px; background-color: #eee; margin: 10px; `; function App() { const [items, setItems] = useState<string[]>([]); const [page, setPage] = useState(1); useEffect(() => { const fetchData = async () => { const response = await axios.get(`https://jsonplaceholder.typicode.com/photos?_page=${page}&_limit=10`); setItems(prevItems => [...prevItems, ...response.data.map((item: any) => item.url)]); }; fetchData(); }, [page]); const handleScroll = () => { const { scrollTop, scrollHeight, clientHeight } = document.documentElement; if (scrollTop + clientHeight >= scrollHeight - 100) { setPage(prevPage => prevPage + 1); } }; useEffect(() => { window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, []); return ( <Container> {items.map(item => ( <Item key={item} style={{ backgroundImage: `url(${item})` }} /> ))} </Container> ); } export default App;
이걸 같은 환경에서 실행시켜 보았다.
스크롤 내리기 전 스크롤 내린 후 스크롤바가 줄어들고 데이터가 계속해서 업데이트 되는 것을 볼 수 있다. 와.... 고대로 베낄수는 없으니 코드를 분석하기로 했다.
2. 분석
const [items, setItems] = useState<string[]>([]); //데이터 저장 const [page, setPage] = useState(1); //현재 페이지 useEffect(() => {//page가 바뀔때마다 데이터 불러오기 const fetchData = async () => { const response = await axios.get(`https://jsonplaceholder.typicode.com/photos?_page=${page}&_limit=10`); setItems(prevItems => [...prevItems, ...response.data.map((item: any) => item.url)]); }; fetchData(); }, [page]);
- useEffect를 사용하여 axios.get 요청을 통해 들어온 데이터가 useState를 사용해 items에 저장을 한다.
- useEffect의 종속성 배열을 사용하여 page가 바뀔때 호출하도록 되어있다.
useEffect(() => { // 스크롤링 감지 이벤트 window.addEventListener("scroll", handleScroll); return () => window.removeEventListener("scroll", handleScroll); }, []); const handleScroll = () => { // 스크롤링 감지 콜백 함수 const { scrollTop, scrollHeight, clientHeight } = document.documentElement; if (scrollTop + clientHeight >= scrollHeight - 100) { setPage((prevPage) => prevPage + 1); } };
- window.addEventListener()는 첫번째 인자로 "scroll"을 받고 있기 때문에 scroll할 때마다 콜백 함수가 작동한다.
- window.addEventListener()의 두번째 인자는 콜백 함수이다.
- 해당 페이지를 떠날 경우 window.removeEventListener()를 사용하여 이벤트를 없앤다.
- scrollTop은 요소의 가장 위부터 스크롤을 내린 위치의 픽셀 수를 나타낸다.(스크롤을 할 수 없는 경우 0이다.)
- scrollHeight는 overflow로 보이지 않는 content를 포함한 content의 높이를 나타낸다.
- clientHeight는 content의 내부 높이를 나타낸다. => overflow로 보이지 않는 부분은 포함되지 않음.
- 즉, 스크롤 바의 높이 + 보이는 부분의 content 이 전체 content의 높이 - 100보다 크거나 같을 경우 page가 +1된다. => 데이터 받아오는 useEffect가 호출된다. => 데이터가 업데이트 된다.
3. 적용
우리 프로젝트의 입맛에 맞게 조금 고쳐서 적용해 봤다!
const [items, setItems] = useState<any[]>([]); // axios로 받아온 데이터 저장 const [isPage, setPage] = useState(1); // 현재 페이지 저장 useEffect(() => { // 처음 데이터 받아오고 현재 페이지가 바뀔때 데이터 받아오고 items에 저장 const fetchData = async () => { const res = await axios.get(`http://localhost:3001/data/${isPage}`); setItems((prev) => [...prev, ...res.data.data]); }; fetchData(); }, [isPage]); const handleScroll = () => { // 스크롤 위치 감지 콜백 함수 const { scrollTop, scrollHeight, clientHeight } = document.documentElement; // 마지막 페이지 조건 추가 if (scrollTop + clientHeight >= scrollHeight - 500) { setPage((prev) => prev + 1); } }; useEffect(() => { window.addEventListener("scroll", handleScroll); return () => window.removeEventListener("scroll", handleScroll); }, []);
출처: https://abangpa1ace.tistory.com/118
'project > main' 카테고리의 다른 글
github action 환경 변수 설정 (0) 2023.03.19 11일 차 (서버와 연결) (0) 2023.03.17 9일 차 (모달창 반응형 + 모달창 페이지 전환) (0) 2023.03.15 8일 차 - 이미지 업로드 일부 구현(Toast UI로 통신 결과 받는 것 까지!) (0) 2023.03.14 7일차 (0) 2023.03.13