/* eslint-disable no-console,react-hooks/exhaustive-deps,@typescript-eslint/no-explicit-any */ 'use client'; import { Suspense, useCallback, useEffect, useRef, useState } from 'react'; import { getShortDramaList, getShortDramaLatest, ShortDramaItem, } from '@/lib/shortdrama.client'; import DoubanCardSkeleton from '@/components/DoubanCardSkeleton'; import PageLayout from '@/components/PageLayout'; import ShortDramaSelector from '@/components/ShortDramaSelector'; import VideoCard from '@/components/VideoCard'; function ShortDramaPageClient() { const [shortDramaData, setShortDramaData] = useState([]); const [loading, setLoading] = useState(false); const [currentPage, setCurrentPage] = useState(1); const [hasMore, setHasMore] = useState(true); const [isLoadingMore, setIsLoadingMore] = useState(false); const [totalPages, setTotalPages] = useState(1); const observerRef = useRef(null); const loadingRef = useRef(null); const debounceTimeoutRef = useRef(null); // 选择器状态 const [selectedCategory, setSelectedCategory] = useState('0'); // '0' 表示全部 // 用于存储最新参数值的 refs const currentParamsRef = useRef({ selectedCategory: '0', currentPage: 1, }); // 生成骨架屏数据 const skeletonData = Array.from({ length: 25 }, (_, index) => index); // 同步最新参数值到 ref useEffect(() => { currentParamsRef.current = { selectedCategory, currentPage, }; }, [selectedCategory, currentPage]); // 参数快照比较函数 const isSnapshotEqual = useCallback( ( snapshot1: { selectedCategory: string; currentPage: number; }, snapshot2: { selectedCategory: string; currentPage: number; } ) => { return ( snapshot1.selectedCategory === snapshot2.selectedCategory && snapshot1.currentPage === snapshot2.currentPage ); }, [] ); // 防抖的数据加载函数 const loadInitialData = useCallback(async () => { // 创建当前参数的快照 const requestSnapshot = { selectedCategory, currentPage: 1, }; try { setLoading(true); // 确保在加载初始数据时重置页面状态 setShortDramaData([]); setCurrentPage(1); setHasMore(true); setIsLoadingMore(false); let data: ShortDramaItem[] = []; let totalPages = 1; if (selectedCategory === '0') { // 全部分类 - 调用获取最新剧集的接口 const latestData = await getShortDramaLatest({ page: '1' }); data = Array.isArray(latestData) ? latestData : []; totalPages = 10; // 假设最新剧集有多页 } else { // 其他分类 - 调用获取分类热搜的接口 const response = await getShortDramaList({ categoryId: selectedCategory, page: '1', }); data = Array.isArray(response?.list) ? response.list : []; totalPages = response?.totalPages || 1; } // 检查参数是否仍然一致,如果一致才设置数据 const currentSnapshot = { ...currentParamsRef.current }; if (isSnapshotEqual(requestSnapshot, currentSnapshot)) { setShortDramaData(data); setTotalPages(totalPages); setHasMore(data.length !== 0 && currentPage < totalPages); setLoading(false); } else { // 没有更多数据时设置hasMore为false setHasMore(false); } } catch (err) { console.error('加载短剧数据失败:', err); setLoading(false); // 发生错误时总是停止loading状态 } }, [selectedCategory, isSnapshotEqual]); // 加载数据 useEffect(() => { // 清除之前的防抖定时器 if (debounceTimeoutRef.current) { clearTimeout(debounceTimeoutRef.current); } // 使用防抖机制加载数据,避免连续状态更新触发多次请求 debounceTimeoutRef.current = setTimeout(() => { loadInitialData(); }, 100); // 100ms 防抖延迟 // 清理函数 return () => { if (debounceTimeoutRef.current) { clearTimeout(debounceTimeoutRef.current); } }; }, [selectedCategory, loadInitialData]); // 单独处理 currentPage 变化(加载更多) useEffect(() => { if (currentPage > 1) { const fetchMoreData = async () => { // 创建当前参数的快照 const requestSnapshot = { selectedCategory, currentPage, }; try { setIsLoadingMore(true); let data: ShortDramaItem[] = []; if (selectedCategory === '0') { // 全部分类 - 调用获取最新剧集的接口 const latestData = await getShortDramaLatest({ page: currentPage.toString() }); data = Array.isArray(latestData) ? latestData : []; } else { // 其他分类 - 调用获取分类热搜的接口 const response = await getShortDramaList({ categoryId: selectedCategory, page: currentPage.toString(), }); data = Array.isArray(response?.list) ? response.list : []; } // 检查参数是否仍然一致,如果一致才设置数据 const currentSnapshot = { ...currentParamsRef.current }; if (isSnapshotEqual(requestSnapshot, currentSnapshot)) { setShortDramaData((prev) => [...prev, ...data]); setHasMore(data.length !== 0 && currentPage < totalPages); } else { // 参数不一致,忽略此次响应 console.log('参数已变更,忽略过期的数据响应'); } } catch (err) { console.error('加载更多短剧数据失败:', err); } finally { setIsLoadingMore(false); } }; fetchMoreData(); } }, [currentPage, selectedCategory, totalPages, isSnapshotEqual]); // 设置滚动监听 useEffect(() => { // 如果没有更多数据或正在加载,则不设置监听 if (!hasMore || isLoadingMore || loading) { return; } // 确保 loadingRef 存在 if (!loadingRef.current) { return; } const observer = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting && hasMore && !isLoadingMore) { setCurrentPage((prev) => prev + 1); } }, { threshold: 0.1 } ); observer.observe(loadingRef.current); observerRef.current = observer; return () => { if (observerRef.current) { observerRef.current.disconnect(); } }; }, [hasMore, isLoadingMore, loading]); // 处理选择器变化 const handleCategoryChange = useCallback( (category: string) => { if (category !== selectedCategory) { setLoading(true); setCurrentPage(1); setShortDramaData([]); setHasMore(true); setIsLoadingMore(false); setSelectedCategory(category); } }, [selectedCategory] ); return (
{/* 页面标题和选择器 */}
{/* 页面标题 */}

短剧

精彩短剧,尽在掌握

{/* 选择器组件 */}
{/* 内容展示区域 */}
{/* 内容网格 */}
{loading ? // 显示骨架屏 skeletonData.map((index) => ) : // 显示实际数据 Array.isArray(shortDramaData) && shortDramaData.length > 0 ? shortDramaData.map((item, index) => { const videoId = item.vod_id ? item.vod_id.toString() : item.id.toString(); return (
); }) : null}
{/* 加载更多指示器 */} {hasMore && !loading && (
{ if (el && el.offsetParent !== null) { ( loadingRef as React.MutableRefObject ).current = el; } }} className='flex justify-center mt-12 py-8' > {isLoadingMore && (
加载中...
)}
)} {/* 没有更多数据提示 */} {!hasMore && shortDramaData.length > 0 && (
已加载全部内容
)} {/* 空状态 */} {!loading && (!Array.isArray(shortDramaData) || shortDramaData.length === 0) && (
暂无相关内容
)}
); } export default function ShortDramaPage() { return ( ); }