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

为何绑定div元素后useRef对象仍为null?(无限滚动场景)

问题分析与解决方案

核心问题

  1. 同一ref绑定多个元素:你将refetchTrigger同时绑定到renderDiv返回的div和循环渲染的每个TrackGridItem外层div,导致ref的current值被反复覆盖,无法稳定指向目标触发元素。
  2. IntersectionObserver监听失效:useIntersection的useEffect依赖为空数组,仅在组件初始化时执行一次,此时ref尚未关联DOM元素,后续元素挂载后也不会重新创建监听。
  3. 状态更新闭包问题:更新list时直接使用[...list, ...tracks],闭包会捕获旧的list值,可能导致数据累加错误。
  4. 无效日志时机:renderDiv内的日志在JSX创建阶段执行,此时元素还未挂载到DOM,ref的current必然是null,该日志无实际参考意义。

修复后的代码

1. 修正TrackGrid组件

export const TrackGrid = () => {
  const [list, setList] = useState<Track[]>([]);
  const [page, setPage] = useState<number>(1);
  const refetchTrigger = useRef<HTMLDivElement | null>(null);
  const inViewport = useIntersection(refetchTrigger, "0px");

  const { tracks, error, isValidating } = useGetTracks(false, page, 20);

  useEffect(() => {
    // 加入isValidating判断,避免重复发起请求
    if (inViewport && !isValidating) {
      setPage(prev => prev + 1);
    }
  }, [inViewport, isValidating]);

  useEffect(() => {
    if (tracks) {
      // 用函数式更新获取最新状态,避免闭包问题
      setList(prevList => [...prevList, ...tracks]);
    }
  }, [tracks]);

  return (
    <>
      <div className="grid place-items-center grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
        {list.map((track: Track, i: number) => (
          <div key={i}>
            <TrackGridItem track={track} />
          </div>
        ))}
      </div>
      {/* 将触发加载的div单独放在列表末尾,仅绑定一次ref */}
      {list.length > 0 && <div ref={refetchTrigger}>加载中...</div>}
    </>
  );
};

2. 修正useIntersection钩子

export const useIntersection = (element: React.RefObject<HTMLDivElement>, rootMargin: string) => {
  const [isVisible, setState] = useState<boolean>(false);

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        setState(entry.isIntersecting);
      },
      { rootMargin }
    );

    const currentElement = element.current;
    if (currentElement) {
      observer.observe(currentElement);
    }

    return () => {
      if (currentElement) {
        observer.unobserve(currentElement);
      }
    };
  }, [element, rootMargin]); // 添加依赖,当ref或rootMargin变化时重新监听

  return isVisible;
};

关键修复点说明

  • 单一ref绑定:仅将refetchTrigger绑定到列表末尾的加载触发div,确保ref始终指向唯一目标元素。
  • 更新监听依赖:为useIntersection的useEffect添加element和rootMargin依赖,当ref关联的DOM元素变化时自动重建监听。
  • 函数式状态更新:更新list和page时使用函数式更新,避免闭包导致的旧状态引用问题。
  • 防重复请求:通过isValidating状态判断,避免同一时间发起多个重复的分页请求。

内容的提问来源于stack exchange,提问作者Richard Ryan Garcia

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 15:20:21