首页 >web前端 >js教程 >Reactjs 教程:使用 Intersection Observer 进行无限滚动。

Reactjs 教程:使用 Intersection Observer 进行无限滚动。

Patricia Arquette
Patricia Arquette原创
2024-12-17 19:45:11175浏览

什么是无限滚动以及它的必要性?

滚动是水平或垂直移动网页上部分内容的用户操作(在大多数情况下)。

就像您在阅读本文时所做的那样。

无限意味着当您向下滚动网页时,新内容会自动加载。

好吧,但是为什么每个人都应该实现它?

可发现性

让我们想象一下您最喜欢的电子商务商店正在举办黑色星期五促销活动。

您在探索页面上找到了几个产品,但当您滚动到网页底部而不是更多产品时,您发现了一个按钮,可将您带到下一个产品列表。

您将能够看到新产品(但前提是您注意到该操作按钮)。

无限滚动只是帮助用户找到更多他们可能错过的内容。

执行

为了实现无限滚动,我们需要检查用户是否到达页面底部或容器。

但是检测滚动的位置是非常昂贵的,并且由于不同的浏览器和设备,其位置值不可靠。

所以一种方法是观看页面的最后内容(元素)及其与视口或容器的交点

我们如何找到交点?

路口观察者

它是一个 Web API,允许观察内容或列表末尾的元素

当这个元素(“哨兵”)变得可见(与视口相交时,它会触发回调函数

通过这个函数我们可以获取更多数据并将其加载到网页中。

整个观察是异步发生的,这最小化对主线程的影响。


为了在 Reactjs 中实现 Intersection Observer,我们将以社交提要为例,我们将在帖子列表上进行无限滚动。

看一下这个组件,您就可以了解下面每个部分的详细情况。

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

interface IIntersectionObserverProps {}

const allItems = [
  "https://picsum.photos/200",
  "https://picsum.photos/200",
  "https://picsum.photos/200",
  "https://picsum.photos/200",
];

const IntersectionObserverImplement: React.FunctionComponent<
  IIntersectionObserverProps
> = (props) => {
  const cardRefs = useRef<(HTMLDivElement | null)[]>([]); // Initialize as an empty array
  const containerRef = useRef<HTMLDivElement | null>(null);
  const [listItems, setListItems] = useState(allItems);

  useEffect(() => {
    const options = {
      root: containerRef.current,
      rootMargin: "0px",
      threshold: 0.5,
    };
    const observer = new IntersectionObserver((entries, observer) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          setListItems((prevItems) => [
            ...prevItems,
            "https://picsum.photos/200",
          ]);
          observer.unobserve(entry.target); // Stop observing the current element
        }
      });
    }, options);

    // Observe the last card only
    const lastCard = cardRefs.current[listItems.length - 1];

    if (lastCard) {
      observer.observe(lastCard);
    }

    return () => observer.disconnect(); // Clean up observer on unmount
  }, [listItems]);

  return (
    <div className="container" ref={containerRef}>
      {listItems.map((eachItem, index) => (
        <div
          className="card"
          ref={(el) => (cardRefs.current[index] = el)} // Assign refs correctly
          key={index}
        >
          <h5>Post {index}</h5>
          <img width={"200"} height={"150"} src={eachItem} />
        </div>
      ))}
    </div>
  );
};

export default IntersectionObserverImplement;

目标是检测提要列表中的最后一个帖子(称为哨兵)何时与视口相交。一旦发生这种情况,就会加载并显示更多帖子。


一个。初始化状态和引用
const cardRefs = useRef<(HTMLDivElement | null)[]>([]); // For storing references to each card
const containerRef = useRef<HTMLDivElement | null>(null); // Reference to the scrollable container
const [listItems, setListItems] = useState(allItems); // State to hold the list of items

cardRefs 一个数组,用于跟踪表示列表中卡片的 DOM 元素。

containerRef 指的是可滚动容器。

listItems 保存页面上当前可见项目的数组。

b.渲染列表并分配引用
return (
  <div className="container" ref={containerRef}>
    {listItems.map((eachItem, index) => (
      <div
        className="card"
        ref={(el) => (cardRefs.current[index] = el)} // Assign a ref to each card
        key={index}
      >
        <h5>Post {index}</h5>
        <img width={"200"} height={"150"} src={eachItem} />
      </div>
    ))}
  </div>
);

containerRef 标记将发生滚动的容器。

cardRefs 为列表中的每张卡片分配一个参考。这确保我们可以告诉观察者要监视哪个元素(例如,最后一张卡片)。

映射 listItems 以呈现列表中的每个项目。
每个 div 都被设计成一张卡片,并且有一个唯一的 React 键。

c.观察最后一个帖子(项目)。
import { useEffect, useRef, useState } from "react";

interface IIntersectionObserverProps {}

const allItems = [
  "https://picsum.photos/200",
  "https://picsum.photos/200",
  "https://picsum.photos/200",
  "https://picsum.photos/200",
];

const IntersectionObserverImplement: React.FunctionComponent<
  IIntersectionObserverProps
> = (props) => {
  const cardRefs = useRef<(HTMLDivElement | null)[]>([]); // Initialize as an empty array
  const containerRef = useRef<HTMLDivElement | null>(null);
  const [listItems, setListItems] = useState(allItems);

  useEffect(() => {
    const options = {
      root: containerRef.current,
      rootMargin: "0px",
      threshold: 0.5,
    };
    const observer = new IntersectionObserver((entries, observer) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          setListItems((prevItems) => [
            ...prevItems,
            "https://picsum.photos/200",
          ]);
          observer.unobserve(entry.target); // Stop observing the current element
        }
      });
    }, options);

    // Observe the last card only
    const lastCard = cardRefs.current[listItems.length - 1];

    if (lastCard) {
      observer.observe(lastCard);
    }

    return () => observer.disconnect(); // Clean up observer on unmount
  }, [listItems]);

  return (
    <div className="container" ref={containerRef}>
      {listItems.map((eachItem, index) => (
        <div
          className="card"
          ref={(el) => (cardRefs.current[index] = el)} // Assign refs correctly
          key={index}
        >
          <h5>Post {index}</h5>
          <img width={"200"} height={"150"} src={eachItem} />
        </div>
      ))}
    </div>
  );
};

export default IntersectionObserverImplement;

选项对象

const cardRefs = useRef<(HTMLDivElement | null)[]>([]); // For storing references to each card
const containerRef = useRef<HTMLDivElement | null>(null); // Reference to the scrollable container
const [listItems, setListItems] = useState(allItems); // State to hold the list of items

root 指定滚动容器。

containerRef.current 指的是包裹所有卡片的 div。
如果 root 为 null,则默认观察视口。

rootMargin:定义根周围的额外边距。

“0px”表示没有多余的空间。您可以使用“100px”之类的值来提前触发观察者(例如,当元素即将出现时)。

阈值:确定观察者触发时目标元素必须可见的程度。

0.5 表示当最后一张卡片的 50% 可见时触发回调。

创建观察者

return (
  <div className="container" ref={containerRef}>
    {listItems.map((eachItem, index) => (
      <div
        className="card"
        ref={(el) => (cardRefs.current[index] = el)} // Assign a ref to each card
        key={index}
      >
        <h5>Post {index}</h5>
        <img width={"200"} height={"150"} src={eachItem} />
      </div>
    ))}
  </div>
);

IntersectionObserver 接受回调函数和之前定义的选项对象。

每当观察到的元素满足选项中指定的条件时,回调就会运行。

entries 参数是观察到的元素的数组。每个条目都包含有关元素是否相交(可见)的信息。

如果entry.isIntersecting为true,则意味着最后一张卡片现在可见:

  1. 使用 setListItems 将新项目添加到列表中。
  2. 取消观察当前元素(entry.target)以防止冗余触发器。

观察最后一张牌

 useEffect(() => {
    const options = {
      root: containerRef.current,
      rootMargin: "0px",
      threshold: 0.5,
    };
    const observer = new IntersectionObserver((entries, observer) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          setListItems((prevItems) => [
            ...prevItems,
            "https://picsum.photos/200",
          ]);
          observer.unobserve(entry.target); // Stop observing the current element
        }
      });
    }, options);

    // Observe each card
    const lastCard = cardRefs.current[listItems.length - 1];

    if (lastCard) {
      observer.observe(lastCard);
    }

    return () => observer.disconnect(); // Clean up observer on unmount
  }, [listItems]);

cardRefs.current:跟踪对所有卡片的引用。

listItems.length - 1:标识列表中的最后一项。

如果lastCard存在,使用observer.observe(lastCard)开始观察它。

观察者会监听这张卡片,并在它可见时触发回调。

清理

const options = {
  root: containerRef.current, // Observe within the container
  rootMargin: "0px",         // No margin around the root container
  threshold: 0.5,           // Trigger when 50% of the element is visible
};

observer.disconnect() 删除此 useEffect 创建的所有观察者。

这确保了当组件卸载或重新渲染时,旧的观察者被清理。


Reactjs Tutorial : Infinite scrolling with Intersection Observer.

每个阶段会发生什么?

1。用户滚动

当用户滚动时,最后一张卡片进入视图

2。路口观察者触发器

当最后一张牌的50%可见时,观察者的回调
运行。

3。添加项目

回调将新项目添加到列表中 (setListItems)。

4。重复

观察者与旧的最后一张卡断开连接并附加到
新的最后一张卡。

Reactjs Tutorial : Infinite scrolling with Intersection Observer.

这就是我们如何使用 Intersection Observer.

实现无限滚动

希望这对您有帮助:)

谢谢。

以上是Reactjs 教程:使用 Intersection Observer 进行无限滚动。的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn