pref: 优化 useEffect 依赖和清理逻辑
This commit is contained in:
@@ -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) {
|
||||
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 (
|
||||
<Card ref={ref} {...rest} bg={cardBg} borderColor={borderColor} mb={4}>
|
||||
|
||||
@@ -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<boolean>} 是否加载成功
|
||||
*/
|
||||
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
|
||||
});
|
||||
|
||||
// 只在组件仍挂载时更新状态
|
||||
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: '所有页面均已缓存'
|
||||
});
|
||||
|
||||
// 只在组件仍挂载时更新状态
|
||||
if (isMountedRef.current) {
|
||||
setCurrentPage(newPage);
|
||||
}
|
||||
} else {
|
||||
// 场景D: 意外分支(有缺失页面但 hasMore=false)
|
||||
logger.warn('DynamicNewsCard', '意外分支:有缺失页面但无法加载', {
|
||||
@@ -294,6 +329,8 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t
|
||||
cachedCount
|
||||
});
|
||||
|
||||
// 只在组件仍挂载时更新状态
|
||||
if (isMountedRef.current) {
|
||||
setCurrentPage(newPage);
|
||||
|
||||
toast({
|
||||
@@ -305,6 +342,7 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t
|
||||
position: 'top'
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [
|
||||
currentPage,
|
||||
pageSize,
|
||||
|
||||
Reference in New Issue
Block a user