Skip to content

[URH-12] useIntersectionObserver 신규 #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jul 30, 2024

Conversation

foresec
Copy link
Collaborator

@foresec foresec commented Jul 12, 2024

👾 Pull Request

  • 작업명: use-intersection-observer
  • 상태: 신규

1️⃣ Spec

  • IntersectionObserver API를 이용하여 요소의 가시성을 감지하는 커스텀 훅
  • 기본적인 Intersection Observer API의 옵션값(root, rootMargin, threshold)과 부가적인 옵션값 및 실행할 콜백함수를 받음
  • 감지할 요소의 상태를 설정하는 함수, 현재 감지 여부, 교차상태에 대한 자세한 관련 정보를 나타내는 객체를 반환

2️⃣ 변경 사항

  • 변경 사항 없음

3️⃣ 예시 코드

1. 기본

  const { intersectionRef, isView, entry } = useIntersectionObserver();
 <div ref={intersectionRef}></div>
  • intersectionRef : 훅에서 사용할 요소의 상태를 설정하는 데 사용되는 디스패치 함수. 감지하고자 하는 요소에 설정
  • isView : 요소가 현재 뷰포트 내에 보이는지 여부를 나타내는 상태 값
  • entry : Intersection Observer API를 사용하여 요소의 교차 상태와 관련된 정보를 나타내는 객체

2. IntersectionObserver API 기본 옵션

  const { intersectionRef, isView } = useIntersectionObserver({
    root: document.querySelector('#container') 
    rootMargin: '10px 20px 30px 40px' 
    threshold: 0.5,
  });
  • root : 뷰포트 대신 사용할 요소 객체 지정. 기본적으로 브라우저의 Viewport가 사용됨(null)
  • rootMargin : Root의 범위를 확장하거나 축소시킴
  • threshold : observer가 실행되기 위한 최소한의 타켓의 가시성 비율

3. 부가옵션

  const { intersectionRef, isView } = useIntersectionObserver({
    initialView: true,
    visibleOnce: true,
  });
  • initialView : 초기 감지값
  • visibleOnce : 처음 한번만 감지

4. callback 함수

  const { intersectionRef, isView, entry } = useIntersectionObserver({
    onChange: (isView) => {
      if (isView) {
        console.log('visible');
      } else {
        console.log('x');
      }
    },
    onEnter() {
      console.log('onEnter');
    },
    onLeave() {
      console.log('onLeave');
    },
  });
  • onChange : 타겟 엘리먼트의 가시성 상태가 변경될 때 호출할 콜백 함수
  • onEnter : 타겟 엘리먼트가 화면에 나타날 때 호출할 콜백 함수
  • onLeave : 타겟 엘리먼트가 화면에서 사라질 때 호출할 콜백 함수

4️⃣ 관련 문서 (선택 사항)

@foresec foresec self-assigned this Jul 12, 2024
Copy link
Member

@jeongbaebang jeongbaebang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

작성해주신 훅 잘 보았습니다. 실제로 사용해보면 더 이해가 잘 될 것 같은데, 전체적인 예시 코드를 작성해주실 수 있을까요?

@foresec
Copy link
Collaborator Author

foresec commented Jul 17, 2024

작성해주신 훅 잘 보았습니다. 실제로 사용해보면 더 이해가 잘 될 것 같은데, 전체적인 예시 코드를 작성해주실 수 있을까요?

해당 훅을 사용하기 위해서

  1. 먼저 감지하고자 하는 Element에 intersectionRef를 설정합니다.
  <div ref={intersectionRef}> </div>
  1. isView와 entry를 활용하여 해당 ref가 감지된 여부와 자세 값을 반환받습니다.

entry의 경우,
image
이와 같은 값을 받는데, 사실 자세한 사항을 자주 사용하는 경우를 보지는 못했기 때문에 활용성이라기보다는 기본적으로 관련 API에서 제공하는 값이기 때문에 자주 쓰이는 isIntersectingisView의 형태로 따로 분리하고 전체 entry 또한 함께 제공합니다.

그외 변수

  • IntersectionObserver API 기본 옵션의 경우, 문서에도 링크를 남겨뒀는데 https://heropy.blog/2019/10/27/intersection-observer/ 에서 그림으로 설명이 아주 잘 정리되어 있습니다.
  • initialView, visibleOnce의 경우 초기값 설정 / 한번만 실행되고 더이상 실행되지 않는 기본적인 사항을 추가한 것입니다.

onChange 대표적인 활용 예시

대표적인 활용 예시로는 다음과 같이 Lazy Loading을 수행할 수 있습니다

  1. intersectionRef가 붙은 영역이 viewport에 보이면
  2. onChange콜백함수가 작동하여 3개의 item을 추가합니다
const Temp = () => {
  const nextItemRef = useRef(1);
  const [items, setItems] = useState<number[]>([]);
  const [loading, setLoading] = useState(false);

  const fetchItems = async () => {
    return new Promise<number[]>((resolve) => {
      setTimeout(() => {
        const newItems = [
          nextItemRef.current,
          nextItemRef.current + 1,
          nextItemRef.current + 2,
        ];
        nextItemRef.current += 3;
        resolve(newItems);
      }, 1000);
    });
  };

  const handleIntersectionChange = async (isView: boolean) => {
    if (isView && !loading) {
      setLoading(true);
      try {
        const newItems = await fetchItems();
        setItems((prevItems) => [...prevItems, ...newItems]);
        setLoading(false);
      } catch (error) {
        console.error(error);
        setLoading(false);
      }
    }
  };

  const { intersectionRef } = useIntersectionObserver({
    threshold: 0.5,
    onChange: handleIntersectionChange,
  });

  return (
    <div>
      <div style={{ height: "1000px" }}></div>
      <div
        style={{
          padding: "30px",
          backgroundColor: "lightblue",
        }}
      >
        {items.map((item, index) => (
          <div
            key={index}
            style={{
              margin: "10px",
              height: "20px",
              backgroundColor: "lightyellow",
              padding: "10px",
            }}
          >
            Item {item}
          </div>
        ))}
        {loading && <p>Loading more items...</p>}
      </div>

      <div ref={intersectionRef}> </div>
    </div>
  );
};

export default Temp;
2024-07-17.194139.mp4

onEnter, onLeave

onEnteronLeave의 경우 해당 Element가 Viewport에 진입했을 경우와 사라졌을 경우에만 작동하는 콜백함수인데
onChange와 달리 좀 더 간단한 로직일때 쓸 일이 있지 않을까 해서 작성했습니다.

  const handleIntersectionChange = async () => {
    if (!loading) {
      setLoading(true);
      try {
        const newItems = await fetchItems();
        setItems((prevItems) => [...prevItems, ...newItems]);
        setLoading(false);
      } catch (error) {
        console.error(error);
        setLoading(false);
      }
    }
  };
  const { intersectionRef, isView, entry } = useIntersectionObserver({
    onEnter: handleIntersectionChange,
  });

필요에 따라 이와 같이 isView를 제외하고 위onChange를 활용한 동일한 lazyLoading을 수행할 수도 있습니다.

3가지 콜백함수를 비교하면 다음과 같습니다

  const { intersectionRef } = useIntersectionObserver({
    onChange() {
      console.log("CHANGE");
    },
    onEnter() {
      console.log("ENTER");
    },
    onLeave() {
      console.log("LEAVE");
    },
  });
2024-07-17.202535.mp4

@jeongbaebang
Copy link
Member

작성해주신 훅 잘 보았습니다. 실제로 사용해보면 더 이해가 잘 될 것 같은데, 전체적인 예시 코드를 작성해주실 수 있을까요?

해당 훅을 사용하기 위해서

  1. 먼저 감지하고자 하는 Element에 intersectionRef를 설정합니다.

...

상세한 설명 감사합니다 😊
고생하셨습니다!

@foresec foresec changed the title Feature/use intersection observer [URH-27] useIntersectionObserver 신규 Jul 19, 2024
@foresec foresec changed the title [URH-27] useIntersectionObserver 신규 [URH-12] useIntersectionObserver 신규 Jul 19, 2024
Copy link
Member

@Choozii Choozii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤩

Copy link
Member

@bicochan bicochan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다! 🚀

@foresec foresec merged commit 62a451e into master Jul 30, 2024
@foresec foresec deleted the feature/use-intersection-observer branch July 30, 2024 12:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants