+GraphQL-無限スクロールのTypescriptを作成


無限スクロールをしたいです。

  • より前にポスターで作成した反応項目は、無限スクロールを実現したい.
  • を参照
    https://velog.io/@mkh1213/React-GraphQL-Typescript-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-4
  • 既存転送フォルダ構造
  • ページのフォルダでInfinityScrollを使用します.tsx
  • を追加
  • gqlフォルダでInfinityScrollを使用します.gql.ts
  • を追加
  • コンポーネントフォルダのUserInfo.tsx、ユーザーリスト
  • を追加

    ログインページ

  • 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関数は、画面の開始時にのみ実行され、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にはタイムスタンプ値が含まれているため、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;
    作成
  • ユーザ情報
  • 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
            }
        }
    `

    実装結果



    サーバコードを作成...


    勉強して...