带有 React 和 Hooks 的 IntersectionObserver

paw*_*l_s 4 javascript reactjs intersection-observer react-hooks

我正在尝试使用 React/Hooks 和 Intersection Observer API 跟踪元素可见性。但是,我不知道如何使用“useEffect”设置观察。有谁知道我该怎么做?我的解决方案不起作用...

function MainProvider({ children }) {
  const [targetToObserve, setTargetToObserve] = useState([]);

  window.addEventListener("load", () => {
    const findTarget = document.querySelector("#thirdItem");
    setTargetToObserve([findTarget]);
  });

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.intersectionRatio === 0.1) {
          console.log("It works!");
        }
      },
      {
        root: null,
        rootMargin: "0px",
        threshold: 0.1
      }
    );
    if (targetToObserve.current) {
      observer.observe(targetToObserve.current);
    }
  }, []);

  return (
    <main>
     <div className="Section-item" id="firstItem"></div>
     <div className="Section-item" id="secondItem"></div>
     <div className="Section-item" id="thirdItem"></div>
    </main>
  );
}
Run Code Online (Sandbox Code Playgroud)

Fil*_*ski 34

JavaScript

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

export function useOnScreen(ref) {
  const [isOnScreen, setIsOnScreen] = useState(false);
  const observerRef = useRef(null);

  useEffect(() => {
    observerRef.current = new IntersectionObserver(([entry]) =>
      setIsOnScreen(entry.isIntersecting)
    );
  }, []);

  useEffect(() => {
    observerRef.current.observe(ref.current);

    return () => {
      observerRef.current.disconnect();
    };
  }, [ref]);

  return isOnScreen;
}


Run Code Online (Sandbox Code Playgroud)

用法:

import { useRef } from 'react';
import useOnScreen from './useOnScreen';

function MyComponent() {
  const elementRef = useRef(null);
  const isOnScreen = useOnScreen(elementRef);

  console.log({isOnScreen});

  return (
    <div>
      <div style={{ paddingBottom: '140vh' }}>scroll to element...</div>
      <div ref={elementRef}>my element</div>
    </div>
  );
}

Run Code Online (Sandbox Code Playgroud)

打字稿

import { useEffect, useState, useRef, RefObject } from 'react';

export default function useOnScreen(ref: RefObject<HTMLElement>) {
  const observerRef = useRef<IntersectionObserver | null>(null);
  const [isOnScreen, setIsOnScreen] = useState(false);

  useEffect(() => {
    observerRef.current = new IntersectionObserver(([entry]) =>
      setIsOnScreen(entry.isIntersecting)
    );
  }, []);

  useEffect(() => {
    observerRef.current.observe(ref.current);

    return () => {
      observerRef.current.disconnect();
    };
  }, [ref]);

  return isOnScreen;
}

Run Code Online (Sandbox Code Playgroud)

用法:

import { useRef } from 'react';
import useOnScreen from './useOnScreen';

function MyComponent() {
  const elementRef = useRef<HTMLDivElement>(null);
  const isOnScreen = useOnScreen(elementRef);

  console.log({isOnScreen});

  return (
    <div>
      <div style={{ paddingBottom: '140vh' }}>scroll to element...</div>
      <div ref={elementRef}>my element</div>
    </div>
  );
}

Run Code Online (Sandbox Code Playgroud)

https://codesandbox.io/s/useonscreen-uodb1?file=/src/useOnScreen.ts


Cre*_*rge 8

这是一个可重用的钩子,它正在使用refuseEffect清理功能来防止挂载/卸载大量组件时的内存泄漏

钩子

function useOnScreen(ref) {

  const [isIntersecting, setIntersecting] = useState(false)

  const observer = new IntersectionObserver(
    ([entry]) => setIntersecting(entry.isIntersecting)
  )

  useEffect(() => {
    observer.observe(ref.current)
    return () => {
      observer.disconnect()
    }
  }, [])

  return isIntersecting
}
Run Code Online (Sandbox Code Playgroud)

在组件中的使用

function DumbComponent() {

  const ref = useRef()

  const onScreen = useOnScreen(ref)

  return <div ref={ref}>{onScreen && "I'm on screen!"}</div>
}
Run Code Online (Sandbox Code Playgroud)

  • 我相信这个实现会在每个渲染上创建一个新的 IntersectionObserver。请参阅答案 /sf/answers/4747823881/ 了解避免这种情况的方法。 (2认同)

paw*_*l_s 7

需要使用 React.useRef() 而不是 addEventListener('load', function() ),因为 eventListener 将在屏幕上出现某些内容之前运行。

import React, { useRef, useEffect } from 'react'

function MainProvider({ children }) {
  const ref = useRef();

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        console.log(entry);

        if (entry.isIntersecting) {
          //do your actions here
          console.log('It works!')
        }
      },
      {
        root: null,
        rootMargin: "0px",
        threshold: 0.1
      }
    );
    if (ref.current) {
      observer.observe(ref.current);
    }
  }, [ref]);

  return (
    <main>
     <div className="Section-item" id="firstItem"></div>
     <div className="Section-item" ref={ref} id="secondItem"></div>
     <div className="Section-item" id="thirdItem"></div>
    </main>
  );
}
Run Code Online (Sandbox Code Playgroud)