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

React Hooks中setOffset无法更新状态,无限滚动重复获取数据

React无限滚动offset不更新问题修复

问题原因

这是React闭包陷阱导致的:你的useEffect依赖是空数组,里面绑定的handleScroll和fetchData都是第一次渲染时创建的,它们永远捕获初始的offset值(0)。哪怕调用setOffset更新了值,这些旧函数里的offset还是0,所以每次请求用的都是同一个offset,拿到重复数据。

修复方案

方案1:用useRef保存最新offset(推荐,性能更优)

useRef的.current属性会始终同步最新的state值,不受闭包捕获旧值的影响。

  1. 新增offsetRef跟踪最新offset:
const [offset, setOffset] = useState(0);
const offsetRef = useRef(offset);
  1. 同步offset到ref:
useEffect(() => {
  offsetRef.current = offset;
}, [offset]);
  1. 修改createURL使用offsetRef.current:
const createURL = () => {
  const url = new URL(API_BASE_URL);
  url.searchParams.set('q', 'hello');
  url.searchParams.set('api_key', API_KEY);
  url.searchParams.set('offset', offsetRef.current);
  return url;
}

方案2:通过useCallback更新函数依赖

让fetchData和handleScroll随offset变化重新创建,确保捕获最新的offset值:

  1. 用useCallback包裹fetchData:
const fetchData = useCallback(async () => {
  try {
    const response = await fetch(createURL());
    const { data, pagination } = await response.json();
    setImages(prevImages => [...prevImages, ...data]);
    setOffset(prevOffset => prevOffset + 1);
  } catch(error) {
    console.error(error);
  }
}, [offset]);
  1. 用useCallback包裹handleScroll:
const handleScroll = useCallback(() => {
  if (window.innerHeight + document.documentElement.scrollTop !== document.documentElement.offsetHeight) {
    return;
  }
  fetchData();
}, [fetchData]);
  1. 更新useEffect的依赖数组:
useEffect(() => {
  fetchData();
  window.addEventListener('scroll', handleScroll);
  return () => window.removeEventListener('scroll', handleScroll);
}, [fetchData, handleScroll]);

完整修复代码(方案1)

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

function App() {
  const API_KEY = '***';
  const API_BASE_URL = 'https://api.giphy.com/v1/gifs/search';
  const [images, setImages] = useState([]);
  const [offset, setOffset] = useState(0);
  const offsetRef = useRef(offset);

  // 同步offset到ref
  useEffect(() => {
    offsetRef.current = offset;
  }, [offset]);

  const handleScroll = () => {
    if (window.innerHeight + document.documentElement.scrollTop !== document.documentElement.offsetHeight) {
      return;
    }
    fetchData();
  }

  const createURL = () => {
    const url = new URL(API_BASE_URL);
    url.searchParams.set('q', 'hello');
    url.searchParams.set('api_key', API_KEY);
    url.searchParams.set('offset', offsetRef.current);
    return url;
  }

  const fetchData = async () => {
    try {
      const response = await fetch(createURL());
      const { data, pagination } = await response.json();
      setImages(prevImages => [...prevImages, ...data]);
      setOffset(prevOffset => prevOffset + 1);
    } catch(error) {
      console.error(error);
    }
  }

  useEffect(() => {
    fetchData();
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, [])

  return (
    <div className="container">
      <h1>Testimonials</h1>
      <div className="testimonialContainer">
        {images.map((image,idx) => {
          return (
            <p key={idx} className="testimonial">{image.title}</p>
          )
        })}
      </div>
    </div>
  );
}

export default App;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 09:47:01