实现无限滚动触底仅发起一次API调用并移除事件监听器的问题
无限滚动功能异常修复方案
问题根因
- 偏移量传参错误:当前把分页累加值
pageOfmembers传给了接口的limit字段,而接口要求limit固定为20、offset才是需要累加的偏移量,参数传反导致偏移逻辑完全失效。 - 重复请求无锁机制:触底判断后加了500ms延迟请求,未加请求中状态锁,滚动事件多次触发就会重复发起调度。同时
next为null时仅做了请求拦截,未真正移除滚动监听器,事件仍会持续触发。 - 触底判断逻辑缺陷:用
==判断滚动高度,浏览器滚动像素存在精度误差时可能触发异常,同时Reducer中GET_MEMBERS_OF_GROUP逻辑冲突,先拼接列表又在page=0时覆盖,容易导致数据异常。 - 闭包问题:滚动事件回调依赖的state可能存在旧值,导致判断逻辑异常。
修复代码
1. 调整Reducer逻辑
case GET_MEMBERS_OF_GROUP: { newState["nextMembers"] = action.payload.response.next; // 先判断是否是首页,再处理数据拼接,避免逻辑冲突 if(action.payload.page == 0){ newState["members"] = action.payload.response.results || []; } else { newState["members"] = newState["members"].concat(action.payload.response.results || []); } return newState; } case SHOW_NEXT_MEMBERS: { newState["pageOfmembers"] = newState["pageOfmembers"] + 20; // 固定每次偏移+20,无需额外传参 return newState; }
2. 调整组件滚动逻辑
// 新增请求锁状态,防止重复触发 const [isLoading, setIsLoading] = useState(false); const [nextmembers, setNextmembers] = useState(data.nextMembers); // 用ref存储最新状态,避免闭包问题 const nextRef = useRef(nextmembers); const loadingRef = useRef(isLoading); // 同步ref值 useEffect(() => { nextRef.current = data.nextMembers; setNextmembers(data.nextMembers); }, [data.nextMembers]); useEffect(() => { loadingRef.current = isLoading; }, [isLoading]); // 滚动监听逻辑 useEffect(() => { // 所有数据加载完成直接返回不绑定事件,原有绑定事件自动解绑 if (!nextmembers) return; const handleScroll = () => { // 用>=判断触底,预留5px容错空间,加请求锁拦截 if ( window.innerHeight + window.scrollY >= document.body.scrollHeight - 5 && nextRef.current && !loadingRef.current && window.location.href.includes("/members") ) { setIsLoading(true); // 先更新偏移量 dispatch({ type: SHOW_NEXT_MEMBERS }); // 传参修正:limit固定20,offset为累加的pageOfmembers,可按接口实际参数名调整 dispatch(EMDoGetMembersOfGroupAction({ id: data.singleGroup.id || window.sessionStorage.getItem("GroupId"), limit: 20, offset: data.pageOfmembers })).finally(() => { // 请求完成后解锁 setIsLoading(false); }); } }; window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, [nextmembers, dispatch, data.singleGroup.id, data.pageOfmembers]);
优化说明
- 新增
isLoading请求锁,同一时间只会有一个分页请求运行,避免重复调用 - 当
nextmembers为null时自动解绑滚动监听器,符合加载完成后移除监听的需求 - 修正接口传参逻辑,解决偏移量不递增的问题
- 触底判断预留容错空间,避免浏览器像素精度问题导致的触发异常
- 用ref存储实时状态,解决滚动事件回调的闭包问题
内容的提问来源于stack exchange,提问作者Suraj Pawar
相关产品推荐
相关产品推荐

