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

React中条件渲染加载更多元素时IntersectionObserver不触发问题

问题核心原因

你遇到的问题本质是条件渲染导致被IntersectionObserver监听的DOM节点不存在,或监听逻辑没有和节点渲染时机同步:

  1. 如果你给Loading...外层加了仅在加载时才渲染的判断,那么非加载状态下被监听的节点直接从DOM树中移除,IntersectionObserver没有可监听的目标,自然不会触发
  2. 如果你用普通ref + 初始化只执行一次的useEffect绑定监听,首次渲染时如果节点还没渲染出来,ref是空值,监听逻辑根本没有生效,后续节点渲染出来也不会自动重新绑定

修复方案

1. 改用回调Ref绑定监听,保证节点渲染时自动绑定监听

用useCallback包裹ref回调逻辑,只要被监听的节点渲染到页面上,就会自动执行回调完成监听绑定,同时会在节点销毁前自动清理旧的监听实例,避免内存泄漏。

import { useState, useRef, useCallback, useEffect } from 'react';

function App() {
  const [page, setPage] = useState(1);
  const [isLoading, setIsLoading] = useState(false);
  const [numberOfPages, setNumberOfPages] = useState(0);
  const [listData, setListData] = useState([]);
  const observer = useRef(null);

  // 回调Ref,节点渲染时自动绑定监听
  const loadMoreRef = useCallback(node => {
    // 正在加载中时不重复绑定
    if (isLoading) return;
    // 先销毁之前的监听实例
    if (observer.current) observer.current.disconnect();

    observer.current = new IntersectionObserver(entries => {
      // 只有节点进入视口、还有下一页、非加载状态时才触发加载
      if (entries[0].isIntersecting && page < numberOfPages && !isLoading) {
        setIsLoading(true);
        // 调用接口拉取下一页数据
        fetch(`你的接口地址?page=${page + 1}`)
          .then(res => res.json())
          .then(data => {
            setListData(prev => [...prev, ...data.list]);
            setNumberOfPages(data.totalPages);
            setPage(prev => prev + 1);
            setIsLoading(false);
          });
      }
    });

    // 节点存在时启动监听
    if (node) observer.current.observe(node);
  }, [isLoading, page, numberOfPages]);

  // 首次加载第一页数据
  useEffect(() => {
    setIsLoading(true);
    fetch(`你的接口地址?page=1`)
      .then(res => res.json())
      .then(data => {
        setListData(data.list);
        setNumberOfPages(data.totalPages);
        setIsLoading(false);
      });
  }, []);

  return (
    <div className="App">
      {/* 表格/列表内容 */}
      {listData.map(item => (
        <div key={item.id} className="list-item">{item.content}</div>
      ))}
      {/* 监听节点:只要还有下一页就保留DOM,仅加载时显示Loading文本 */}
      {page < numberOfPages && (
        <div ref={loadMoreRef} className="load-more-trigger">
          {isLoading ? "Loading..." : null}
        </div>
      )}
    </div>
  );
}

2. 调整条件渲染逻辑

不要把加载节点的渲染和isLoading状态绑定,只要还有下一页数据,就保留这个监听节点的DOM存在:

  • 加载中时节点内显示Loading...文本
  • 非加载状态下可以留空或者设置成高度极小的透明占位元素,不影响视觉效果,同时保证IntersectionObserver有可监听的目标
  • 只有当前页等于总页数时,才销毁这个节点,停止触发加载逻辑

注意事项

  • 每次绑定新的监听实例前,必须调用disconnect销毁旧的实例,避免重复监听
  • 触发加载的判断里必须加上!isLoading的校验,避免同一时间发起多个重复请求
  • 接口返回数据后要及时更新isLoading状态,避免后续监听被阻塞

内容的提问来源于stack exchange,提问作者jarivak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 20:06:04