当前位置: 首页 > article >正文

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 (
        <div style={{ height: "400px", overflow: "auto", border: "1px solid #ddd", padding: "10px" }}>
            <List
                dataSource={data}
                renderItem={(item, index) => <List.Item key={index}>{index}{item.detailText}</List.Item>}
            />
            <div ref={containerRef} style={{ textAlign: "center", padding: "10px" }}>
                {loading ? <Spin /> : hasMore ? "滚动加载中..." : "没有更多数据了"}
            </div>
        </div>
    );
};

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 = (
        <div style={{ width: "300px", maxHeight: "400px", overflow: "auto", border: "1px solid #ddd", padding: "10px" }}>
            <List
                dataSource={data}
                renderItem={(item, index) => <List.Item key={index}>{index} {item.detailText}</List.Item>}
            />
            <div ref={containerRef} style={{ textAlign: "center", padding: "10px" }}>
                {loading ? <Spin /> : hasMore ? "滚动加载中..." : "没有更多数据了"}
            </div>
        </div>
    );
    const handleScroll = (e) => {
        const { scrollTop, clientHeight, scrollHeight } = e.target;
        if (scrollTop + clientHeight >= scrollHeight - 10 && hasMore && !loading) {
            setPage(prevPage => prevPage + 1);
        }
    };

    return (
        <Popover
            title="数据列表"
            trigger="click"
            content={
                <div
                    style={{ width: "300px", maxHeight: "400px", overflowY: "auto" }}
                    onScroll={handleScroll} // 监听滚动
                >
                    <List
                        dataSource={data}
                        renderItem={(item, index) => <List.Item key={index}>{index} {item.detailText}</List.Item>}
                    />
                    <div style={{ textAlign: "center", padding: "10px" }}>
                        {loading ? <Spin /> : hasMore ? "滚动加载中..." : "没有更多数据了"}
                    </div>
                </div>
            }
        >
            <Button type="primary">查看数据</Button>
        </Popover>

    );
};

export default InfiniteScrollPopover;

 


http://www.kler.cn/a/599076.html

相关文章:

  • 【无标题】vue项目,浏览器打印时,永远只显示一页的问题
  • JSX入门
  • 第31章:Istio安全:mTLS与服务间身份认证
  • Python爬虫获取Shopee店铺的所有商品?
  • git使用经验(一)
  • 算法方法快速回顾
  • leetcode 的T5 最长回文字符串
  • 【Linux之Shell脚本实战】Linux服务器输出美观漂亮的html巡检报告
  • 4.4 前缀和专题:LeetCode 238. 除自身以外数组的乘积
  • 企业级前端架构设计与实战
  • 3.23 代码随想录第二十四天打卡
  • armsom产品qt交叉编译
  • 算法模型从入门到起飞系列——背包问题(探索最大价值的掘金之旅)
  • C# 资源管理‌(using 语句)
  • vscode中latex的tex文件和pdf跳转
  • (一)飞行器的姿态欧拉角, 欧拉旋转, 完全数学推导(基于坐标基的变换矩阵).(偏航角,俯仰角,横滚角)
  • 区块链交易
  • 火绒终端安全管理系统V2.0——行为管理(软件禁用+违规外联)
  • 【Leetcode】430. 扁平化多级双向链表
  • SFT和RLHF是什么意思?