diff --git a/src/views/Community/components/DynamicNewsCard.js b/src/views/Community/components/DynamicNewsCard.js index 675b14ce..b9bc38e5 100644 --- a/src/views/Community/components/DynamicNewsCard.js +++ b/src/views/Community/components/DynamicNewsCard.js @@ -1,7 +1,7 @@ // src/views/Community/components/DynamicNewsCard.js // 横向滚动事件卡片组件(实时要闻·动态追踪) -import React, { forwardRef, useState, useEffect, useMemo, useCallback } from 'react'; +import React, { forwardRef, useState, useEffect, useMemo, useCallback, useRef } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { Card, @@ -72,6 +72,9 @@ const DynamicNewsCard = forwardRef(({ // 本地状态 const [selectedEvent, setSelectedEvent] = useState(null); + // 初始化标记 - 确保初始加载只执行一次 + const hasInitialized = useRef(false); + // 使用分页 Hook const { currentPage, @@ -91,9 +94,10 @@ const DynamicNewsCard = forwardRef(({ toast }); - // 初始加载 + // 初始加载 - 只在组件首次挂载且未初始化时执行 useEffect(() => { - if (allCachedEvents.length === 0) { + if (!hasInitialized.current && allCachedEvents.length === 0) { + hasInitialized.current = true; dispatch(fetchDynamicNews({ page: PAGINATION_CONFIG.INITIAL_PAGE, per_page: PAGINATION_CONFIG.CAROUSEL_PAGE_SIZE, @@ -103,12 +107,27 @@ const DynamicNewsCard = forwardRef(({ } }, [dispatch, allCachedEvents.length]); - // 默认选中第一个事件 + // 默认选中第一个事件 - 只在当前选中的事件不在当前页时重置 useEffect(() => { - if (currentPageEvents.length > 0 && !selectedEvent) { - setSelectedEvent(currentPageEvents[0]); + if (currentPageEvents.length > 0) { + // 检查当前选中的事件是否在当前页中 + const selectedEventInCurrentPage = currentPageEvents.find( + e => e.id === selectedEvent?.id + ); + + // 如果选中的事件不在当前页,则选中当前页第一个事件 + if (!selectedEventInCurrentPage) { + setSelectedEvent(currentPageEvents[0]); + } } - }, [currentPageEvents, selectedEvent]); + }, [currentPageEvents, selectedEvent?.id]); + + // 组件卸载时清理选中状态 + useEffect(() => { + return () => { + setSelectedEvent(null); + }; + }, []); return ( diff --git a/src/views/Community/components/DynamicNewsCard/hooks/usePagination.js b/src/views/Community/components/DynamicNewsCard/hooks/usePagination.js index eae13809..4c6f0e4d 100644 --- a/src/views/Community/components/DynamicNewsCard/hooks/usePagination.js +++ b/src/views/Community/components/DynamicNewsCard/hooks/usePagination.js @@ -1,7 +1,7 @@ // src/views/Community/components/DynamicNewsCard/hooks/usePagination.js // 分页逻辑自定义 Hook -import { useState, useMemo, useCallback } from 'react'; +import { useState, useMemo, useCallback, useRef, useEffect } from 'react'; import { fetchDynamicNews } from '../../../../../store/slices/communityDataSlice'; import { logger } from '../../../../../utils/logger'; import { @@ -22,11 +22,21 @@ import { * @returns {Object} 分页状态和方法 */ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, toast }) => { + // 组件挂载状态跟踪 - 用于防止内存泄漏 + const isMountedRef = useRef(true); + // 本地状态 const [currentPage, setCurrentPage] = useState(PAGINATION_CONFIG.INITIAL_PAGE); const [loadingPage, setLoadingPage] = useState(null); const [mode, setMode] = useState(DEFAULT_MODE); + // 组件卸载时更新挂载状态 + useEffect(() => { + return () => { + isMountedRef.current = false; + }; + }, []); + // 根据模式决定每页显示数量 const pageSize = mode === DISPLAY_MODES.CAROUSEL ? PAGINATION_CONFIG.CAROUSEL_PAGE_SIZE @@ -167,6 +177,12 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t * @returns {Promise} 是否加载成功 */ const loadPages = useCallback(async (missingPages, targetPage, silentMode = false) => { + // 检查组件是否已卸载 + if (!isMountedRef.current) { + logger.debug('DynamicNewsCard', '组件已卸载,取消加载'); + return false; + } + if (!silentMode) { // 显示 loading 状态 setLoadingPage(targetPage); @@ -182,6 +198,12 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t // 拆分为单页请求,避免 per_page 动态值导致后端返回空数据 for (const page of missingPages) { + // 每次请求前检查组件是否已卸载 + if (!isMountedRef.current) { + logger.debug('DynamicNewsCard', '组件已卸载,中止加载'); + return false; + } + logger.debug('DynamicNewsCard', `开始加载第 ${page} 页`); await dispatch(fetchDynamicNews({ @@ -207,8 +229,8 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t missingPages }); - if (!silentMode) { - // 非静默模式下显示错误提示 + // 只在组件仍挂载时显示错误提示 + if (!silentMode && isMountedRef.current) { toast({ title: '加载失败', description: `无法加载第 ${targetPage} 页数据,请稍后重试`, @@ -221,8 +243,8 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t return false; } finally { - if (!silentMode) { - // 清除加载状态 + // 只在组件仍挂载时清除加载状态 + if (!silentMode && isMountedRef.current) { setLoadingPage(null); } } @@ -230,6 +252,12 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t // 翻页处理(智能预加载)- 使用子函数重构 const handlePageChange = useCallback(async (newPage) => { + // 检查组件是否已卸载 + if (!isMountedRef.current) { + logger.debug('DynamicNewsCard', '组件已卸载,取消翻页'); + return; + } + // 🔍 诊断日志 - 记录翻页开始状态 logger.debug('DynamicNewsCard', '开始翻页', { currentPage, @@ -260,7 +288,10 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t 缺失页面: missingPages }); - setCurrentPage(newPage); + // 只在组件仍挂载时更新状态 + if (isMountedRef.current) { + setCurrentPage(newPage); + } await loadPages(missingPages, newPage, true); // 静默模式 } else if (missingPages.length > 0 && hasMore) { // 场景B: 目标页未缓存,显示 loading 并等待加载完成 @@ -271,7 +302,8 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t }); const success = await loadPages(missingPages, newPage, false); // 非静默模式 - if (success) { + // 只在加载成功且组件仍挂载时更新状态 + if (success && isMountedRef.current) { setCurrentPage(newPage); } } else if (missingPages.length === 0) { @@ -282,7 +314,10 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t reason: '所有页面均已缓存' }); - setCurrentPage(newPage); + // 只在组件仍挂载时更新状态 + if (isMountedRef.current) { + setCurrentPage(newPage); + } } else { // 场景D: 意外分支(有缺失页面但 hasMore=false) logger.warn('DynamicNewsCard', '意外分支:有缺失页面但无法加载', { @@ -294,16 +329,19 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t cachedCount }); - setCurrentPage(newPage); + // 只在组件仍挂载时更新状态 + if (isMountedRef.current) { + setCurrentPage(newPage); - toast({ - title: '数据不完整', - description: `第 ${newPage} 页数据可能不完整`, - status: 'warning', - duration: TOAST_CONFIG.DURATION_WARNING, - isClosable: true, - position: 'top' - }); + toast({ + title: '数据不完整', + description: `第 ${newPage} 页数据可能不完整`, + status: 'warning', + duration: TOAST_CONFIG.DURATION_WARNING, + isClosable: true, + position: 'top' + }); + } } }, [ currentPage,