From d96ebd6b8ceebe986181ea6c799df42562e07e26 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Wed, 5 Nov 2025 08:39:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=9B=E5=BB=BA=E6=97=A0=E9=99=90?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8Hook=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=E2=94=82=20=E2=94=82=20?= =?UTF-8?q?=E2=94=82=20=E2=94=82=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=E2=94=82=20=E2=94=82=20=E2=94=82=20?= =?UTF-8?q?=E2=94=82=20-=20=E7=9B=91=E5=90=AC=E5=AE=B9=E5=99=A8=E6=BB=9A?= =?UTF-8?q?=E5=8A=A8=E4=BA=8B=E4=BB=B6=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=E2=94=82=20=E2=94=82=20?= =?UTF-8?q?=E2=94=82=20=E2=94=82=20-=20=E8=B7=9D=E7=A6=BB=E5=BA=95?= =?UTF-8?q?=E9=83=A8=E9=98=88=E5=80=BC=E5=8F=AF=E9=85=8D=E7=BD=AE=EF=BC=88?= =?UTF-8?q?=E9=BB=98=E8=AE=A4200px=EF=BC=89=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=E2=94=82=20=E2=94=82=20=E2=94=82=20=E2=94=82=20-=20=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E8=A7=A6=E5=8F=91onLoadMore=E5=9B=9E=E8=B0=83=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=E2=94=82=20?= =?UTF-8?q?=E2=94=82=20=E2=94=82=20=E2=94=82=20-=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E7=8A=B6=E6=80=81=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hooks/useInfiniteScroll.js | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/views/Community/components/DynamicNewsCard/hooks/useInfiniteScroll.js diff --git a/src/views/Community/components/DynamicNewsCard/hooks/useInfiniteScroll.js b/src/views/Community/components/DynamicNewsCard/hooks/useInfiniteScroll.js new file mode 100644 index 00000000..17ccfb8c --- /dev/null +++ b/src/views/Community/components/DynamicNewsCard/hooks/useInfiniteScroll.js @@ -0,0 +1,88 @@ +// src/views/Community/components/DynamicNewsCard/hooks/useInfiniteScroll.js +// 无限滚动 Hook + +import { useEffect, useRef, useCallback } from 'react'; + +/** + * 无限滚动 Hook + * 监听容器滚动事件,当滚动到底部附近时触发加载更多数据 + * + * @param {Object} options - 配置选项 + * @param {Function} options.onLoadMore - 加载更多回调函数(返回 Promise) + * @param {boolean} options.hasMore - 是否还有更多数据 + * @param {boolean} options.isLoading - 是否正在加载 + * @param {number} options.threshold - 触发阈值(距离底部多少像素时触发,默认200px) + * @returns {Object} { containerRef } - 容器引用 + */ +export const useInfiniteScroll = ({ + onLoadMore, + hasMore = true, + isLoading = false, + threshold = 200 +}) => { + const containerRef = useRef(null); + const isLoadingRef = useRef(false); + + // 滚动处理函数 + const handleScroll = useCallback(() => { + const container = containerRef.current; + + // 检查条件:容器存在、未加载中、还有更多数据 + if (!container || isLoadingRef.current || !hasMore) { + return; + } + + const { scrollTop, scrollHeight, clientHeight } = container; + const distanceToBottom = scrollHeight - scrollTop - clientHeight; + + // 距离底部小于阈值时触发加载 + if (distanceToBottom < threshold) { + console.log( + '%c⬇️ [懒加载] 触发加载下一页', + 'color: #8B5CF6; font-weight: bold;', + { + scrollTop, + scrollHeight, + clientHeight, + distanceToBottom, + threshold + } + ); + + isLoadingRef.current = true; + + // 调用加载函数并更新状态 + onLoadMore() + .then(() => { + console.log('%c✅ [懒加载] 加载完成', 'color: #10B981; font-weight: bold;'); + }) + .catch((error) => { + console.error('%c❌ [懒加载] 加载失败', 'color: #DC2626; font-weight: bold;', error); + }) + .finally(() => { + isLoadingRef.current = false; + }); + } + }, [onLoadMore, hasMore, threshold]); + + // 绑定滚动事件 + useEffect(() => { + const container = containerRef.current; + if (!container) return; + + // 添加滚动监听 + container.addEventListener('scroll', handleScroll, { passive: true }); + + // 清理函数 + return () => { + container.removeEventListener('scroll', handleScroll); + }; + }, [handleScroll]); + + // 更新 loading 状态的 ref + useEffect(() => { + isLoadingRef.current = isLoading; + }, [isLoading]); + + return { containerRef }; +};