본문 바로가기
개발일지/React

React 무한스크롤 만들어보기

by 한삐 2022. 9. 20.
728x90

개발공부를 하다보니 무한스크롤을 사용하는 웹사이트를 들어가면 기능이나 사용감을 생각해보게 된다.

데이터를 효율적으로 처리하는 방법 중 하나라고는 하지만

사용자 입장에서도 부드러운 사용감이 생기지 않나 싶다.

 

간단하게 패키지 하나를 설치하고 시작해보겠다.

 

yarn add react-intersection-observer

이름 그대로 요소를 관찰해주는 친구다

(target element or element의 부모 or 뷰포트가 교차하는 부분 등 관찰)

 

 -- 글작성 페이지 생략 --

 

// 데이터가 있다고 가정했을때
// TestInfi.jsx

import { useEffect, useState, useRef, useCallback } from "react";
import { useInView } from "react-intersection-observer";

import axios from "axios";

export default function TestInfi() {
  

  const [isLoading, setIsLoading] = useState(false);

  // 무한스크롤 ##################################################

  const [posts, setPosts] = useState([]);
  const [hasNextPage, setHasNextPage] = useState(true);
  const page = useRef(1);
  const [ref, inView] = useInView(true);

  const fetch = useCallback(async () => {
    try {
      // 데이터 패칭을 시작하면 로딩 상태를 로딩중으로 변환
      setIsLoading(true)
      const { data } = await axios.get(
      	// 데이터 요청 url의 파라미터로 limit , page 값을 넣어준다.
        `http://localhost:3001/posts?_limit=20&_page=${page.current}`
      );
      // 로딩중을 표시할 div가 보일 시간을 주기 위한 setTimeout
      setTimeout(()=>{
        setPosts((prevPosts) => [...prevPosts, ...data]);
      } ,1500)
      // 패칭이 실행되면 다음에 실행될 페이지를 1page씩 늘려준다.
      setHasNextPage(data.length === 20);
      if (data.length) {
        page.current += 1;
      }
    } catch (err) {
      console.error(err);
    }
    // 로딩중을 표시할 div가 보일 시간을 주기 위한 setTimeout
    setTimeout(()=>{
      setIsLoading(false)
    } ,1500)
  }, []);

  // inView(useState)를 통해 useEffect 실행
  useEffect(() => {
    console.log(inView, hasNextPage);
    if (inView && hasNextPage) {
      fetch();
    }
  }, [fetch, hasNextPage, inView]);

  console.log(posts);
  //############################################

  return (
    <div>
      {posts?.map((item) => (
        <div key={item.id} style={{ marginBottom: "40px" }}>
          <div>아이디 : {item.id}</div>
          <div>닉네임 : {item.nickName}</div>
          <div>내용 : {item.comment}</div>
        </div>
      ))}
      // 눈에 안보이는 ref div - position으로 밑에 위치시켜준다.
      <div ref={ref} style={{ position: "absolute" }} />
      // 로딩일때 보여줄거예요
      {isLoading===true?<div>로딩중이에옹</div>:null}
    </div>
  );
}

 

위와 같이 설정해줬을때

무한스크롤이 잘 진행되는걸 볼 수 있다.

 

 

위 코드의 경우, 스크롤을 끝까지 내리지 않으면 로딩중 메세지가 보이지 않는 상태로 데이터가 패칭된다.

사용자가 로딩중인 것을 인지시키기 위해서는, 로딩중인 태그의 스타일을 적절하게 적용시켜주는 것이 좋을 것 같다.

728x90

댓글