分页课程Redux状态重复问题排查求助
无限滚动分页数据重复问题排查与修复
核心问题分析
从你的代码来看,数据重复大概率是同一分页请求被多次触发导致的,结合无限滚动场景,主要有几个触发点:
- 滚动事件连续触发(比如快速滚动到底部),导致
loadClasses被多次调用,而此时前一次请求还未完成,_page状态没更新,会重复请求同一页数据,最终concat到Redux状态里。 - 缺少请求锁机制,没有在请求进行中阻止新的请求。
- Redux-persist的状态恢复时机可能晚于组件初始化的清空操作,导致清空后又被持久化的数据覆盖。
具体修复方案
1. 给请求加上加载锁,阻止重复调用
在自定义hook里,利用Redux的isLoading状态或者本地状态,确保同一时间只有一个分页请求在执行:
export default function useInfiniteClasses( classesRef: any, user: any, _page: any, setPage: any ) { const dispatch = useAppDispatch(); const { isLoading, lastPage } = useAppSelector((state: RootState) => state.classes); const [isRequesting, setIsRequesting] = useState(false); const loadClasses = async () => { // 拦截条件:加载中、无用户信息、已到最后一页 if (isLoading || isRequesting || !user || _page > lastPage) return; setIsRequesting(true); try { if (user?.role === "student") { await dispatch(paginateStudentClasses({ studentId: user?.clientId, page: _page, })); } else if (user?.role === "teacher") { await dispatch(paginateTeacherClasses({ teacherId: user?.clientId, page: _page, })); } setPage(prev => prev + 1); } catch (error) { console.error(error); } finally { setIsRequesting(false); } }; const handleScroll = () => { if (isLoading || isRequesting || _page > lastPage) return; if (Math.floor(classesRef?.current?.scrollLeft + classesRef?.current?.clientWidth) === classesRef?.current?.scrollWidth) { loadClasses(); } }; return { handleScroll, loadClasses }; }
2. 调整Redux-persist清空时机
如果持久化的状态在组件初始化后才恢复,会导致你调用的emptyPaginateClasses被覆盖。可以等待persist完成后再执行初始化操作:
// 组件中添加persist状态监听 const { _persist } = useAppSelector((state: RootState) => state); const initialized = useRef(false); useEffect(() => { // 确保persist恢复完成且只初始化一次 if (_persist?.rehydrated && !initialized.current) { dispatch(emptyPaginateClasses()); loadClasses(); initialized.current = true; } }, [_persist?.rehydrated, dispatch, loadClasses]);
3. 可选:Redux状态更新时去重
如果接口可能返回重复数据,可以在Redux reducer里添加去重逻辑,避免重复数据被存入:
.addCase(paginateStudentClasses.fulfilled, (state: any, action: any) => { console.count("paginate classes!"); const { classes } = action.payload; // 根据课程ID过滤已存在的项 const uniqueClasses = classes.filter(cls => !state.paginateClasses.some(item => item.id === cls.id)); state.paginateClasses = state.paginateClasses.concat(uniqueClasses); state.lastPage = action.payload.lastPage; state.isLoading = false; })
4. 检查组件是否重复挂载
如果组件被父组件频繁触发卸载/挂载,会导致初始化的useEffect多次执行。用useRef标记是否已经初始化过,避免重复执行:
const initialized = useRef(false); useEffect(() => { if (!initialized.current) { dispatch(emptyPaginateClasses()); loadClasses(); initialized.current = true; } }, []);
内容的提问来源于stack exchange,提问作者mohsin syed
相关产品推荐
相关产品推荐

