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

iOS浏览器下useEffect依赖值异常:IntersectionObserver分页问题

iOS浏览器中IntersectionObserver+Redux分页加载的异常问题

在React项目中,我使用IntersectionObserver监听商品列表底部的div,当元素可见时触发数据加载,数据存储在Redux中。点击分类切换函数switchMainCategory()时,activeCategoryId、products和page应更新并触发useEffect调用loadMoreProducts()。该逻辑在桌面及安卓浏览器正常,但iOS浏览器中,loadMoreProducts()会传入旧的activeCategoryId,且page未被设为null而是递增,请问这一iOS特有问题的原因是什么?

核心组件代码

export default function Home() {
   const [isLoadingMore, setIsLoadingMore] = useState(false)
   const { activeCategoryId, products, page } = useSelector((state) => state.pages.homePage)

   const observerTarget = useRef(null)

   useEffect(() => {
    const loadMoreProducts = async () => {
      if (isLoadingMore || page === '') {
        return
      }

      setIsLoadingMore(true)

      const response = await productService.getProducts({
        category: activeCategoryId,
        page: page,
      })

      if (response.isOk && response.data) {
        dispatch(
          PagesActions.setHomePage({
            products: [...products, ...response.data.results],
            page: response.data.next || '',
          })
        )
        // Stop observing if there are no more products to load
        if (response.data.results.length === 0) {
          observer.unobserve(observerTarget.current)
        }
      }

      setIsLoadingMore(false)
    }

    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting) {
          loadMoreProducts()
        }
      },
      { threshold: 1 }
    )

    if (observerTarget.current) {
      observer.observe(observerTarget.current)
    }

    return () => {
      if (observerTarget.current) {
        observer.unobserve(observerTarget.current)
      }
    }
  }, [activeCategoryId, dispatch, page, observerTarget, products, isLoadingMore])

  const switchMainCategory = (category) => {
    dispatch(
      PagesActions.setHomePage({ products: [], page: null, activeCategoryId: category?.code })
    )
  }

  return (
    // here we have a list of categories, list of products and observerTarget div
  )
}

问题原因

1. Safari中IntersectionObserver的闭包快照特性

iOS Safari的IntersectionObserver回调函数会固定捕获闭包创建时的变量值,而非动态获取最新状态。当切换分类触发useEffect重新执行时,虽然创建了新的loadMoreProducts和Observer实例,但旧的Observer可能因为浏览器回收延迟未被彻底销毁,导致旧回调仍被触发,此时它引用的是切换前的activeCategoryId和page。

2. 状态更新与Observer触发的竞态条件

切换分类时,你通过Redux将page设为null,但iOS中可能存在时间差:旧Observer还在监听,新的useEffect销毁旧Observer之前,底部div恰好处于交叉状态,触发了旧的loadMoreProducts。旧函数中的page仍是切换前的数值(并非null),因此会用旧分类ID和旧page请求数据,导致page被错误递增。

3. page判断逻辑的不一致

loadMoreProducts中仅判断page === ''时终止执行,但切换分类时Redux把page设为null,null === ''不成立,所以旧的loadMoreProducts依然会执行请求。这个逻辑漏洞在iOS的竞态场景下被放大,加剧了问题。

内容的提问来源于stack exchange,提问作者Armin Dervić

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 04:00:30