반응형

모든 리소스를 한 번에 로드하는 것이 아니라 사용자가 보는 뷰포트 영역에 접근했을 때 지연로딩하고 싶을 때가 있다.

그 때 사용할 수 있는 API 가 IntersectionObserver API 이다. 뷰포트 영역의 특정 교차점 부분을 진입했을 때 새로운 액션을 할 수 있다.

 

IntersectionObserver() - Web API | MDN (mozilla.org)

 

IntersectionObserver() - Web API | MDN

IntersectionObserver() 생성자는 새로운 IntersectionObserver 객체를 생성하고 반환합니다.

developer.mozilla.org

 

InsersectionObserver 생성자를 살펴보면 첫 번째 인자에는 두 번째 인자(대상 요소의 가시성 비율) 값보다 역치 값이 넘어갈 경우 감시할 옵저버 함수를 만들 수 있다.

 

특정 비율이 넘어갈 때 사진을 로드하는 함수를 만든다고 생각해보자.

 

감시할 imageRef 대상이 있는데 imageSrc 이미지 경로가 없다면, 그 때 생성자를 생성하여 감시를 시작한다.

감시하다가 교차지점에 도달하면 imageSrc 이미지 경로를 세팅한다.

 

import { useState, useRef, useEffect } from "react";

export function useLazyImageObserver({ src }) {
    const [imageSrc, setImageSrc] = useState(null);
    const imageRef = useRef(null);

    useEffect(()=>{
        let observer;
        if (imageRef && !imageSrc) {
            observer = new IntersectionObserver(([entry]) => {
                if (entry.isIntersecting) {
                    setImageSrc(src);
                    observer.unobserve(imageRef.current);
                }
            }, { threshold: [0.25] });
            observer.observe(imageRef.current);
        }
        return () => {
            observer && observer.disconnect(imageRef);
        };
    }, [imageRef, imageSrc, src]);

    return { imageSrc, imageRef };
}

 

[사용부]

import { memo } from "react";
import { useLazyImageObserver } from "../hooks/useLazyImageObserver";

export const LazyImage = memo(({src, alt})=>{
    const { imageSrc, imageRef } = useLazyImageObserver({ src });
    return (
        <img ref={imageRef} src={imageSrc} alt={alt} width='200px' height='300px'/>
    );
});

 

const urlList = [
  'https://picsum.photos/200/300?random=1',
  'https://picsum.photos/200/300?random=2',
  'https://picsum.photos/200/300?random=3',
  'https://picsum.photos/200/300?random=4',
  'https://picsum.photos/200/300?random=5',
  'https://picsum.photos/200/300?random=6',
  'https://picsum.photos/200/300?random=7',
  'https://picsum.photos/200/300?random=8',
  'https://picsum.photos/200/300?random=9',
  'https://picsum.photos/200/300?random=10',
  'https://picsum.photos/200/300?random=11',
  'https://picsum.photos/200/300?random=12',
  'https://picsum.photos/200/300?random=13',
  'https://picsum.photos/200/300?random=14',
  'https://picsum.photos/200/300?random=15',
  'https://picsum.photos/200/300?random=16',
  'https://picsum.photos/200/300?random=17',
  'https://picsum.photos/200/300?random=18',
  'https://picsum.photos/200/300?random=19',
  'https://picsum.photos/200/300?random=20',
  'https://picsum.photos/200/300?random=21',
  'https://picsum.photos/200/300?random=22',
  'https://picsum.photos/200/300?random=23',
  'https://picsum.photos/200/300?random=24',
  'https://picsum.photos/200/300?random=25',
  'https://picsum.photos/200/300?random=26',
  'https://picsum.photos/200/300?random=27',
  'https://picsum.photos/200/300?random=28',
  'https://picsum.photos/200/300?random=29',
  'https://picsum.photos/200/300?random=30',
  'https://picsum.photos/200/300?random=31',
  'https://picsum.photos/200/300?random=32',
  'https://picsum.photos/200/300?random=33',
  'https://picsum.photos/200/300?random=34',
  'https://picsum.photos/200/300?random=35',
  'https://picsum.photos/200/300?random=36',
];

function App() {
  
  return (
    <div style={{ width: '600px' }}>
      {urlList && urlList.map((url, index) => (
          <LazyImage key={index} src={url} alt=""/>
      ))}
    </div>
  );
}

 

반응형

+ Recent posts