+ GraphQL - Typescript 무한스크롤 만들기
무한스크롤을 만들어보려고 한다.
- 이전 포스팅에서 만들어 놓은 리액트 프로젝트로 무한 스크롤을 구현해보려 한다.
- 이 포스팅 참고
https://velog.io/@mkh1213/React-GraphQL-Typescript-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-4 - 기존 포스팅의 폴더 구조
- pages 폴더에 InfinityScroll.tsx 추가
- gql 폴더에 InfinityScroll.gql.ts 추가
- components 폴더에 UserInfo.tsx, UserList 추가
랜딩페이지
- Router.tsx에 무한스크롤 구현할 페이지를 만들어준다.
import React from "react";
import { BrowserRouter , Route, Routes } from "react-router-dom";
import Navigation from "./components/Navigation";
import Home from "./pages/Home";
import Modify from "./pages/Modify";
import SignUp from "./pages/SignUp";
import Study from "./pages/Study";
import InfinityScroll from "./pages/InfinityScroll";
function Router() {
return (
<BrowserRouter >
<Navigation />
<br />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/signup" element={<SignUp />} />
<Route path="/modify" element={<Modify />} />
<Route path="/study" element={<Study />} />
<Route path="/InfinityScroll" element={<InfinityScroll />} />
</Routes>
</BrowserRouter >
);
}
export default Router;
- Navigation.tsx에 무한스크롤 페이지로 갈 수 있도록 링크도 추가해준다.
import React from "react";
import { Link } from "react-router-dom";
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import { useCookies } from "react-cookie";
function Navigation() {
const [cookies] = useCookies(['test']);
return (
<div className="nav">
<Box sx={{ display: 'flex', alignItems: 'center', textAlign: 'center' }}>
<Button>
<Link to="/" style={{ textDecoration: 'none', color: '#1976d2' }}>Home</Link>
</Button>
<Button style={cookies.test === undefined ? {display: 'none'} : {display: ''}}>
<Link to="/modify" style={{ textDecoration: 'none', color: '#1976d2' }}>Modify</Link>
</Button>
<Button>
<Link to="/study" style={{ textDecoration: 'none', color: '#1976d2' }}>Study</Link>
</Button>
<Button>
<Link to="/InfinityScroll" style={{ textDecoration: 'none', color: '#1976d2' }}>InfinityScroll</Link>
</Button>
</Box>
</div>
);
}
export default Navigation;
- 무한스크롤 페이지에 오면 먼저 20개의 데이터를 보여준다.
- getUsersInit함수가 useEffect를 통해 화면 시작 시에만 실행되어 데이터 20건을 가져온다.
- useCallbak으로 스크롤 위치를 감지하여 스크롤이 거의 끝에 왔을 경우에 getUsers 함수로 그 다음 20건을 가져온다.
- 특정 스크롤 에서 함수가 실행되지만, if문으로 다음에 가져올 데이터가 있을 경우에만 가져올 수 있도록 isExistMore을 검사해서 실행한다.
- 데이터를 20건씩 가져오는데, 20건보다 적을 경우 더이상 가져올 데이터가 없으므로 그럴때 false값으로 변경되게끔 한다.
import React, { useCallback, useEffect, useState } from "react";
import { GET_USERS_INIT, GET_USERS } from "../gql/InfinityScroll.gql";
import { useLazyQuery } from "@apollo/client";
import UserList from "../components/UserList";
function InfinityScroll() {
const [users, setUsers] = useState<any>([]);
const [lastId, setLastId] = useState("");
const [isUsersLoading, setIsUsersLoading] = useState<boolean>(false);
const [isExistMore, setIsExistMore] = useState<boolean>(true);
const [getUsersInit, {loading, error}] = useLazyQuery(GET_USERS_INIT, {
fetchPolicy: "cache-and-network",
onError: error => {
console.error(error);
alert(error.message);
},
onCompleted: ({getUsersInit}) => {
setUsers(getUsersInit);
setLastId(getUsersInit[getUsersInit.length - 1]._id);
}
});
const [getUsers, {loading: loading2, error: error2}] = useLazyQuery(GET_USERS, {
fetchPolicy: "cache-and-network",
onError: error => {
console.error(JSON.stringify(error, null, 2))
alert(error.message);
},
onCompleted: ({getUsers}) => {
if (getUsers.length < 20) setIsExistMore(false);
setIsUsersLoading(false);
setLastId(getUsers[getUsers.length - 1]._id);
setUsers([...users, ...getUsers]);
}
});
const handleScroll = useCallback((): void => {
const { innerHeight } = window;
// 브라우저창 내용의 크기 (스크롤을 포함하지 않음)
const { scrollHeight } = document.body;
// 브라우저 총 내용의 크기 (스크롤을 포함한다)
const { scrollTop } = document.documentElement;
// 현재 스크롤바의 위치
if (Math.round(scrollTop + innerHeight) >= scrollHeight) {
// scrollTop과 innerHeight를 더한 값이 scrollHeight보다 크다면, 가장 아래에 도달했다는 의미이다.
if (isExistMore) {
setIsUsersLoading(true);
getUsers({ variables: { lastId } });
}
}
}, [lastId]);
useEffect(() => {
getUsersInit()
}, []);
useEffect(() => {
window.addEventListener('scroll', handleScroll, true);
// 스크롤이 발생할때마다 handleScroll 함수를 호출하도록 추가합니다.
return () => {
window.removeEventListener('scroll', handleScroll, true);
// 해당 컴포넌트가 언마운트 될때, 스크롤 이벤트를 제거합니다.
};
}, [handleScroll]);
return (
<div>
<UserList userData={users} />
</div>
);
}
export default InfinityScroll;
-
데이터의 구성은 _id, email, name인데, _id는 mongodb가 자동으로 만들어주는 난수 값으로 _id 안에 timestamp값이 들어있기 때문에 데이터 20건 중 마지막 값을 서버에 넘겨줌으로 다음 20건을 가져올 수 있도록 한다.
-
UserList 작성
import React from "react";
import UserInfo from "./UserInfo";
function UserList({userData}: any) {
return (
<div>
{userData.map((user: any) => (
<UserInfo
key={user._id}
_id={user._id}
email={user.email}
name={user.name}
/>
))}
</div>
)
}
export default UserList;
- UserInfo 작성
import React from "react";
function UserInfo({_id, email, name}: any) {
return (
<div>
<div style={{border: "1px solid grey", borderRadius: 10, padding: 10, margin: 10}}>
<p>_id: {_id}</p>
<p>email: {email}</p>
<p>name: {name}</p>
</div>
</div>
)
}
export default UserInfo;
- InfinityScroll.gql.ts 작성
import gql from 'graphql-tag';
export const GET_USERS_INIT = gql`
query getUsersInit {
getUsersInit {
_id
email
name
}
}
`
export const GET_USERS = gql`
query getUsers($lastId: String!) {
getUsers(lastId: $lastId) {
_id
email
name
}
}
`
구현 결과
서버 코드 작성...
IntersectionObserver 공부하기...
Author And Source
이 문제에 관하여(+ GraphQL - Typescript 무한스크롤 만들기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@mkh1213/React로-무한-스크롤-만들기저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)