You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现无限滚动触底仅发起一次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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 11:57:03