교차점 관찰자와 반응할 때의 무한 굴림

안녕하세요.
며칠 전, 나는 React에서 무한 스크롤의 용례를 만났다.이를 위해 Intersection Observer를 사용하고 무한 스크롤에서 다른 방법을 찾았습니다.
시작하기 전에 문제 진술을 더 잘 이해합시다.사용자 목록과 기본 세부 사항을 제공하는 API를 고려합니다.이 작업은 카드에 있는 모든 사용자의 목록을 표시하는 것입니다.간단하죠?
현재 수천 명의 사용자가 있다고 가정하면, 우리가 사용하는 API는 페이지로 나뉘어져 있습니다.이 경우 페이지 나누기 API를 사용하는 두 가지 방법이 있습니다.
  • 다음/이전 단추를 사용하여 다른 페이지 탐색
  • 무한 스크롤 사용
  • 글 제목에서 말한 바와 같이 우리는 두 번째 방법을 채택할 것이다.😅
    이제 어떻게 하는지 볼까요?
  • 처음 25개의 결과를 얻기 위해 API를 호출합니다.
  • 사용자가 목록을 스크롤하고 마지막 요소에 도착하면 다른 API 호출을 하고 보기에서 다음 그룹을 끌어옵니다.
  • 이렇게 하면 사용자가 계속 스크롤을 해도 사용자가 마지막에 도착할 때까지 사용자 목록을 볼 수 있다.
    실현 부분에 들어가기 전에 교차점 관찰자를 간단하게 소개하겠습니다.

    무엇이 교차구 관찰자입니까?


    교차점 관찰자는 브라우저 API로, 두 요소의 상호 가시성을 비동기적으로 관찰하거나 탐지하는 방법을 제공합니다.
    MDN에 따르면 이 API는 이미지 로드 지연과'무한 스크롤'웹 사이트 구현 등 가시성과 관련된 작업을 수행하는 데 주로 사용되며, 스크롤할 때 점점 더 많은 내용을 로드하고 렌더링합니다.
    교차로 관찰자here의 상세한 정보를 볼 수 있습니다.

    무한 스크롤 실현


    무한 스크롤에 대해 우리는 소스 오픈 소프트웨어RandomUserAPI를 사용할 것이다.
    기본적인 프로젝트 설정에 대해create React app로 간단한 React 프로젝트를 만들고 Tailwind CSS를 추가했습니다.또한 API를 호출하기 위해 같은 항목에 추가axios했습니다.
    구현 단계는 다음과 같습니다.

    1. API를 호출하여 데이터를 저장하고 표시합니다.


    기본 설정이 완료된 후, 코드의 첫 번째 버전을 봅시다. 여기서 사용자 API를 호출해서 사용자 목록을 가져옵니다.
    // app.js
    import axios from 'axios';
    import { useEffect, useState } from 'react';
    
    const TOTAL_PAGES = 3;
    
    const App = () => {
        const [loading, setLoading] = useState(true);
        const [allUsers, setAllUsers] = useState([]);
        const [pageNum, setPageNum] = useState(1);
    
        const callUser = async () => {
            setLoading(true);
            let response = await axios.get(
                `https://randomuser.me/api/?page=${pageNum}&results=25&seed=abc`
            );
            setAllUsers(response.data.results);
            setLoading(false);
        };
    
        useEffect(() => {
            if (pageNum <= TOTAL_PAGES) {
                callUser();
            }
        }, [pageNum]);
    
        const UserCard = ({ data }) => {
            return (
                <div className='p-4 border border-gray-500 rounded bg-white flex items-center'>
                    <div>
                        <img
                            src={data.picture.medium}
                            className='w-16 h-16 rounded-full border-2 border-green-600'
                            alt='user'
                        />
                    </div>
    
                    <div className='ml-3'>
                        <p className='text-base font-bold'>
                            {data.name.first} {data.name.last}
                        </p>
                        <p className='text-sm text-gray-800'>
                            {data.location.city}, {data.location.country}
                        </p>
                        <p className='text-sm text-gray-500 break-all'>
                            {data.email}
                        </p>
                    </div>
                </div>
            );
        };
    
        return (
            <div className='mx-44 bg-gray-100 p-6'>
                <h1 className='text-3xl text-center mt-4 mb-10'>All users</h1>
    
                <div className='grid grid-cols-3 gap-4'>
                    {allUsers.length > 0 &&
                        allUsers.map((user, i) => {
                            return (
                                <div key={`${user.name.first}-${i}`}>
                                    <UserCard data={user} />
                                </div>
                            );
                        })}
                </div>
                {loading && <p className='text-center'>loading...</p>}
            </div>
        );
    };
    
    export default App;
    
    
    이게 저희 페이지의 모습입니다.👇

    코드는 매우 간단하다.callUser 함수에서 API를 호출하여 결과를 allUsers 상태로 저장합니다.다음은 allUsers 스토리지에서 카드 구성 요소UserCard를 사용하는 모든 사용자를 보여줍니다.
    구성 요소 맨 위에 상수 TOTAL_PAGES 가 정의되어 있는 것을 보실 수 있습니다. 이것은 전체 프로그램에서 반복하고자 하는 페이지의 총 수를 제한하기 위해서입니다.API는 사용 가능한 전체 페이지 수에 대한 세부 정보를 제공하므로 실제 응용 프로그램에서는 필요하지 않습니다.
    또한 페이지 번호를 저장하기 위해 상태를 정의했지만 아직까지 제대로 사용하지 않았다는 것을 알 수 있습니다.교차점 관찰자의 페이지 번호를 바꾸고 싶어서다.

    2. 교차점 관찰자 추가 및 페이지 번호 증가


    무한 스크롤을 하려면 목록의 마지막 요소가 사용자에게 보일 때 페이지 수를 늘려야 합니다.사거리 관찰원이 완성할 것이다.
    우리의 교차점 관찰자는 마지막 요소가 보이는지 관찰할 것이며, 보이면 페이지 번호를 1로 늘릴 것이다.우리의useEffect는 페이지 번호가 바뀔 때 실행되고 API는 호출되기 때문에 더 많은 사용자의 목록을 얻을 것입니다.
    이 논리를 이해한 후에 작업 코드를 봅시다-
    // App.js
    
    const App = () => {
        const [loading, setLoading] = useState(true);
        const [allUsers, setAllUsers] = useState([]);
        const [pageNum, setPageNum] = useState(1);
        const [lastElement, setLastElement] = useState(null);
    
        const observer = useRef(
            new IntersectionObserver(
                (entries) => {
                    const first = entries[0];
                    if (first.isIntersecting) {
                        setPageNum((no) => no + 1);
                    }
                })
        );
    
        const callUser = async () => {
            setLoading(true);
            let response = await axios.get(
                `https://randomuser.me/api/?page=${pageNum}&results=25&seed=abc`
            );
            let all = new Set([...allUsers, ...response.data.results]);
            setAllUsers([...all]);
            setLoading(false);
        };
    
        useEffect(() => {
            if (pageNum <= TOTAL_PAGES) {
                callUser();
            }
        }, [pageNum]);
    
        useEffect(() => {
            const currentElement = lastElement;
            const currentObserver = observer.current;
    
            if (currentElement) {
                currentObserver.observe(currentElement);
            }
    
            return () => {
                if (currentElement) {
                    currentObserver.unobserve(currentElement);
                }
            };
        }, [lastElement]);
    
        const UserCard = ({ data }) => {
            return (
                <div className='p-4 border border-gray-500 rounded bg-white flex items-center'>
                    <div>
                        <img
                            src={data.picture.medium}
                            className='w-16 h-16 rounded-full border-2 border-green-600'
                            alt='user'
                        />
                    </div>
    
                    <div className='ml-3'>
                        <p className='text-base font-bold'>
                            {data.name.first} {data.name.last}
                        </p>
                        <p className='text-sm text-gray-800'>
                            {data.location.city}, {data.location.country}
                        </p>
                        <p className='text-sm text-gray-500 break-all'>
                            {data.email}
                        </p>
                    </div>
                </div>
            );
        };
    
        return (
            <div className='mx-44 bg-gray-100 p-6'>
                <h1 className='text-3xl text-center mt-4 mb-10'>All users</h1>
    
                <div className='grid grid-cols-3 gap-4'>
                    {allUsers.length > 0 &&
                        allUsers.map((user, i) => {
                            return i === allUsers.length - 1 &&
                                !loading &&
                                pageNum <= TOTAL_PAGES ? (
                                <div
                                    key={`${user.name.first}-${i}`}
                                    ref={setLastElement}
                                >
                                    <UserCard data={user} />
                                </div>
                            ) : (
                                <UserCard
                                    data={user}
                                    key={`${user.name.first}-${i}`}
                                />
                            );
                        })}
                </div>
                {loading && <p className='text-center'>loading...</p>}
    
                {pageNum - 1 === TOTAL_PAGES && (
                    <p className='text-center my-10'></p>
                )}
            </div>
        );
    };
    
    코드를 깊이 이해합시다.
    우리는 교차구 관찰자를 정의하여constobserver에 저장했다.교차 관찰자는 모든 교차 대상의 그룹을 받아들이는 리셋 함수를 가지고 있다.그러나 우리는 마지막 요소만 전달하기 때문에, 우리는 항상 이 그룹의 0번째 항목을 검사한다.만약 원소가 교차하여 보이는 것을 의미한다면, 우리는 페이지 번호를 늘릴 것이다.
    우리는 또 하나의 주lastElement를 추가하여 그것을 null로 초기화했다.페이지에서, 우리는 수조의 마지막 요소를 이 상태에 전달할 것이다.
    따라서 lastElement 상태의 값이 변경될 때 다른useEffect (의존 항목 그룹에서 사용 lastElement 를 호출합니다.이런 상황에서 만약에 우리가lastElement의 값을 얻게 된다면 우리는 이 요소를 교점 관찰자에게 전달하여 관찰할 것이다.그 다음에, 우리의 관찰자는 이 요소의 교집합을 검사하고, 이러한 상황이 발생할 때 페이지 계수를 증가시킬 것이다.
    페이지 번호가 변경되면 API가 호출되고 더 많은 사용자가 제공됩니다.새 사용자를 기존 상태에 추가하고 중복을 피하기 위해 작은 변화가 생겼습니다.
    프로그램은 조금도 힘들이지 않고 실행될 것입니다. 당신은 지금 무한 스크롤의 행동을 볼 수 있습니다.🥁
    지금 이대로!만약 당신이 완전한 코드를 보고 싶다면, 당신은 나의 Github repository here 에서 볼 수 있습니다.
    본문을 읽어 주셔서 대단히 감사합니다.이것에 대한 당신의 생각을 알려 주십시오. 만약 당신이 나의 글을 좋아한다면, 당신은 또는 buy me a coffee를 통해 나에게 연락할 수 있습니다.
    *즐거움 코드, 끊임없는 학습🙌 *

    좋은 웹페이지 즐겨찾기