React

[React] 무한스크롤(Infinite Scroll) 구현하기

캐럿노트 2022. 9. 26. 11:39

무한스크롤 구현하기

- test UI

- 더미 데이터는 https://jsonplaceholder.typicode.com/comments에서 가져왔다. 포스터는 images폴더에 따로 넣어놓았다.

Scroll

// Scroll.tsx

import React, { useEffect, useState } from "react";
import InfiniteScroll from "react-infinite-scroll-component";
import "./Scroll.scss";
import Poster from "./Poster";
import Loader from "./Loader";
import EndMessage from "./EndMessage";

function Scroll() {
  const [items, setItems] = useState([]);

  // 끝까지 갈 경우 페이지를 더이상 불러오지 않도록 설정
  const [hasMore, setHasMore] = useState(true);

  const [page, setPage] = useState(2);

  useEffect(() => {
    const getComments = async () => {
      const res = await fetch(
        `https://jsonplaceholder.typicode.com/comments?_page=1&_limit=20`
      );
      const data = await res.json();
      setItems(data);
    };
    getComments();
  }, []);

  const fetchComments = async () => {
    const res = await fetch(
      `https://jsonplaceholder.typicode.com/comments?_page=${page}&_limit=20`
    );
    const data = await res.json();
    return data;
  };

  // fetchData : 화면이 아래까지 도착함을 감지할 경우 다음 페이지를 불러옴
  const fetchData = async () => {
    const commentsFormServer = await fetchComments();

    setItems([...items, ...commentsFormServer]);

    // 페이지를 끝까지 불러왔는지 확인
    if (commentsFormServer.length === 0 || commentsFormServer.length < 20) {
      // 화면이 끝까지 불러올 경우 false로 변경하여 더이상 불러오지 않음
      setHasMore(false);
    }

    setPage(page + 1);
  };
  return (
    <div id="scroll">
      <InfiniteScroll
        className="infinitescroll"
        dataLength={items.length}
        next={fetchData}
        hasMore={hasMore}
        loader={<Loader className="scroll-loader" />}
        endMessage={<EndMessage />}
      >
        <div className="scroll">
          {items.map(item => {
            return <Poster key={item.id} item={item} />;
          })}
        </div>
      </InfiniteScroll>
    </div>
  );
}

export default Scroll;

 

- Loader는 처음에 Material UI 컴포넌트를 사용했으나 오류가 있어 제거했다.

// Loader.tsx

import * as React from "react";

function Loader() {
  return (
    <div>
      <h3>Now Loading...</h3>
    </div>
  );
}
export default Loader;
// EndMessage.tsx

import React from "react";

function EndMessage() {
  return (
    <div>
      <p style={{ textAlign: "center", color: "white" }}>
        <b>End Contents</b>
      </p>
    </div>
  );
}

export default EndMessage;
/* Scroll.scss */

#scroll {
  overflow-y: hidden;
  .infinitescroll {
    overflow-y: hidden;
  }
  .scroll {
    width: 1000px;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    justify-items: center;
  }
}
.infinite-scroll-component {
  overflow-y: hidden;
}

 

Poster

- Poster는 Material UI를 활용했다.

// Poster.jsx

import * as React from "react";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import CardMedia from "@mui/material/CardMedia";
import Typography from "@mui/material/Typography";
import posterSample from "assets/images/posterSample.png";

/* eslint-disable react/prop-types */
function Poster({ item: { id, email, body } }) {
  return (
    <Card id="card" sx={{ width: 200, height: 500, padding: 0, marginTop: 3 }}>
      <CardMedia
        component="img"
        height="200"
        image={posterSample}
        alt="green iguana"
      />
      <CardContent>
        <Typography gutterBottom variant="h5" component="div">
          {id}
        </Typography>
        <Typography variant="body2" color="text.secondary">
          {email}
          <br />
          {body}
        </Typography>
      </CardContent>
    </Card>
  );
}
export default Poster;