and滚动下拉加载

这段代码实现了一个基于 ‌Intersection Observer API‌ 的无限滚动列表组件,结合 React Hooks 和 Ant Design 组件完成数据分页加载。以下是核心逻辑解析:

一、关键功能模块分解

1. 状态管理
const [data, setData] = useState([]);        // 已加载的数据列表
const [loading, setLoading] = useState(false);// 加载中状态
const [page, setPage] = useState(1);          // 当前页码
const [hasMore, setHasMore] = useState(true); // 是否还有更多数据可加载
const containerRef = useRef(null);            // 滚动监听触发的 DOM 元素
2. 数据加载逻辑 (loadData)
const loadData = async () => {
  if (!hasMore || loading) return; // 防御性判断:无更多数据或正在加载时阻止请求
  
  setLoading(true);
  try {
    const params = { 
      current: page, 
      size: PAGE_SIZE,
      // ...其他固定查询参数
    };
    
    const response = await getAllRecords(params); // 发起 API 请求
    const newData = response.data.records || [];
    const total = response.data.total || 0;

    // 判断是否已加载完所有数据
    if (newData.length === 0 || data.length + newData.length >= total) {
      setHasMore(false);
    }

    // 合并新旧数据(函数式更新避免闭包问题)
    setData(prevData => [...prevData, ...newData]);
  } catch (error) {
    console.error("加载失败:", error);
  } finally {
    setLoading(false); // 无论成功失败都关闭加载状态
  }
};
3. 滚动监听机制 (useEffect + Intersection Observer)
useEffect(() => {
  const observer = new IntersectionObserver(([entry]) => {
    // 当触发元素进入视口 + 有更多数据 + 非加载状态时,触发翻页
    if (entry.isIntersecting && hasMore && !loading) {
      setPage(prev => prev + 1); // 页码递增会触发后续的 loadData
    }
  }, { threshold: 1.0 }); // 完全进入视口才触发

  if (containerRef.current) observer.observe(containerRef.current);
  
  return () => observer.disconnect(); // 组件卸载时解除监听
}, [hasMore, loading]); // 依赖项确保状态同步

下面是代码  可以复制一下  这边是react的版本的

import React, { useState, useEffect, useRef } from "react";
import { List, Spin } from "antd";
import { getAllRecords } from "@/services/clockinLog";

const PAGE_SIZE = 20; // 每页加载的数据量


const InfiniteScrollList = () => {
    const [data, setData] = useState([]); // 存储数据
    const [loading, setLoading] = useState(false); // 控制加载状态
    const [page, setPage] = useState(1); // 记录当前页数
    const [hasMore, setHasMore] = useState(true); // 控制是否还有更多数据
    const containerRef = useRef(null); // 监听滚动加载的 DOM

    // **监听 page 变化,触发数据加载**
    useEffect(() => {
        loadData();
    }, [page]); // **监听 page 变化,而不是只在组件挂载时执行**

    useEffect(() => {
        const observer = new IntersectionObserver(([entry]) => {
            if (entry.isIntersecting && hasMore && !loading) {
                setPage(prevPage => prevPage + 1); // **让 `page` 递增,而不是直接调用 `loadData`**
            }
        }, { root: null, rootMargin: "0px", threshold: 1.0 });

        if (containerRef.current) {
            observer.observe(containerRef.current);
        }

        return () => observer.disconnect();
    }, [hasMore, loading]);

    const loadData = async () => {
        if (!hasMore || loading) return; // 避免重复请求
        setLoading(true);

        try {
            const params = {
                current: page, // 传递当前页
                size: PAGE_SIZE,
                clubIdStr: "1827966716816719874,1764481893494001665,1764481818038472705,1762383449803558914,1749705502700343297,1737040398271426562,1737040285528535041,1881224427603558402,1858474790089990146",
                startDate: "2025-01-02 00:00:00",
                endDate: "2025-04-22 23:59:59",
                alarmStatus: 1,
            };

            const response = await getAllRecords(params);
            console.log('getAllRecords:', response);

            const newData = response.data.records || []; // 避免空数据错误
            const total = response.data.total || 0;

            // **如果返回数据为空,说明已经到底了**
            if (newData.length === 0 || data.length + newData.length >= total) {
                setHasMore(false);
            }

            // **使用回调方式更新数据,确保数据不会重复**
            setData(prevData => [...prevData, ...newData]);
        } catch (error) {
            console.error("加载数据失败:", error);
        } finally {
            setLoading(false);
        }
    };

    return (
        
{index}{item.detailText}} />
{loading ? : hasMore ? "滚动加载中..." : "没有更多数据了"}
); }; export default InfiniteScrollList;

 内嵌入的滚动 上方的方法并不适用 可以使用下面的办法 我这边是利用了Popover 里面的 content

可以建议使用下面的办法 

 

import React, { useState, useEffect, useRef } from "react";
import { Popover, List, Spin, Button } from "antd";
import { getAllRecords } from "@/services/clockinLog";

const PAGE_SIZE = 20; // 每页加载的数据量

const InfiniteScrollPopover = () => {
    const [data, setData] = useState([]); // 存储数据
    const [loading, setLoading] = useState(false); // 控制加载状态
    const [page, setPage] = useState(1); // 记录当前页数
    const [hasMore, setHasMore] = useState(true); // 控制是否还有更多数据
    const containerRef = useRef(null); // 监听滚动加载的 DOM

    // **监听 page 变化,触发数据加载**
    useEffect(() => {
        loadData();
    }, [page]); // **监听 page 变化,而不是只在组件挂载时执行**

    useEffect(() => {
        const observer = new IntersectionObserver(([entry]) => {
            if (entry.isIntersecting && hasMore && !loading) {
                setPage(prevPage => prevPage + 1); // **让 `page` 递增,而不是直接调用 `loadData`**
            }
        }, { root: null, rootMargin: "0px", threshold: 1.0 });

        if (containerRef.current) {
            observer.observe(containerRef.current);
        }

        return () => observer.disconnect();
    }, [hasMore, loading]);

    const loadData = async () => {
        if (!hasMore || loading) return; // 避免重复请求
        setLoading(true);

        try {
            const params = {
                current: page, // 传递当前页
                size: PAGE_SIZE,
                clubIdStr: "1827966716816719874,1764481893494001665,1764481818038472705,1762383449803558914,1749705502700343297,1737040398271426562,1737040285528535041,1881224427603558402,1858474790089990146",
                startDate: "2025-01-02 00:00:00",
                endDate: "2025-04-22 23:59:59",
                alarmStatus: 1,
            };

            const response = await getAllRecords(params);
            console.log('getAllRecords:', response);

            const newData = response.data.records || []; // 避免空数据错误
            const total = response.data.total || 0;

            // **如果返回数据为空,说明已经到底了**
            if (newData.length === 0 || data.length + newData.length >= total) {
                setHasMore(false);
            }

            // **使用回调方式更新数据,确保数据不会重复**
            setData(prevData => [...prevData, ...newData]);
        } catch (error) {
            console.error("加载数据失败:", error);
        } finally {
            setLoading(false);
        }
    };

    // 定义 Popover 的内容
    const content = (
        
{index} {item.detailText}} />
{loading ? : hasMore ? "滚动加载中..." : "没有更多数据了"}
); const handleScroll = (e) => { const { scrollTop, clientHeight, scrollHeight } = e.target; if (scrollTop + clientHeight >= scrollHeight - 10 && hasMore && !loading) { setPage(prevPage => prevPage + 1); } }; return ( {index} {item.detailText}} />
{loading ? : hasMore ? "滚动加载中..." : "没有更多数据了"}
} > ); }; export default InfiniteScrollPopover;

 

你可能感兴趣的:(javascript,前端,开发语言)