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

页面加载时React代码重复获取相同数据的问题求助

React商品列表首次加载重复获取相同数据问题

我正在学习React,开发一个商品列表的数据获取功能。预期首次加载时获取2条数据,点击按钮再加载2条更多数据,但目前页面加载时会重复获取相同的2条数据。

import { useEffect, useState, Fragment } from 'react';
import './style.css';

export default function LoadMoreData() {
  const [loading, setLoading] = useState(false);
  const [products, setProducts] = useState([]);
  const [count, setCount] = useState(0);
  const [disableButton, setDisableButton] = useState(false);

  const loadLimit = 10;
  let limit = 2;

  async function fetchProducts() {
    const dataUrl = `https://dummyjson.com/products?limit=${limit}&skip=${
      count === 0 ? 0 : count * limit
    }`;

    try {
      setLoading(true);
      const response = await fetch(dataUrl);
      const result = await response.json();

      if (result && result.products && result.products.length) {
        setProducts((prevData) => [...prevData, ...result.products]);
        setLoading(false);
      }

      console.log(result);
    } catch (e) {
      console.log(e);
      setLoading(false);
    }
  }

  useEffect(() => {
    fetchProducts();
  }, [count]);

  useEffect(() => {
    if (products && products.length === loadLimit) setDisableButton(true);
  }, [products]);

  if (loading) {
    return <div>Loading data! Please wait.</div>;
  }

  return (
    <Fragment>
      <div className='load-more-container'>
        <div className='product-container'>
          {products && products.length
            ? products.map((item) => (
                <div className='product' key={item.id}>
                  <img src={item.thumbnail} alt={item.title} />
                  <p>{item.title}</p>
                  <span>$ {item.price}</span>
                </div>
              ))
            : null}
        </div>
        <div className='button-container'>
          <button disabled={disableButton} onClick={() => setCount(count + 1)}>
            Load more products
          </button>
          {disableButton ? (
            <p>You have reached to {loadLimit} products!.</p>
          ) : null}
        </div>
      </div>
    </Fragment>
  );
}

我曾猜测问题出在将异步函数放在useEffect外部,但即使将其移入useEffect,问题仍未解决。能否有人提示我问题可能出在哪里?谢谢!


问题原因及解决方案

1. 核心问题分析

重复请求的根源有两点:

  • 请求参数逻辑依赖不稳定:你用count计算skip值,但开发环境下React的Strict Mode会触发两次组件挂载,此时count初始值为0,两次useEffect都会发起skip=0的请求,导致获取相同数据。
  • 无重复请求拦截机制:当loading状态未更新完成时,若再次触发请求,会重复调用接口。

2. 具体修复步骤

(1)用现有数据长度计算skip

直接用products.length作为skip值,因为每次加载limit条数据,下一次的起始位置就是当前已加载总数,逻辑更可靠,还能避免count带来的闭包问题:

const dataUrl = `https://dummyjson.com/products?limit=${limit}&skip=${products.length}`;

(2)添加重复请求拦截

在fetchProducts开头判断,若当前处于加载状态则直接返回,避免重复发起请求:

async function fetchProducts() {
  if (loading) return; // 拦截重复请求
  // 原有请求逻辑...
}

(3)优化状态依赖(可选)

count状态可以完全移除,直接在按钮点击时调用fetchProducts即可,同时调整组件挂载时的请求触发逻辑:

// 组件挂载时只请求一次
useEffect(() => {
  fetchProducts();
}, []);

// 按钮点击直接调用请求函数
<button disabled={disableButton || loading} onClick={fetchProducts}>
  Load more products
</button>

修复后的完整代码

import { useEffect, useState, Fragment } from 'react';
import './style.css';

export default function LoadMoreData() {
  const [loading, setLoading] = useState(false);
  const [products, setProducts] = useState([]);
  const [disableButton, setDisableButton] = useState(false);

  const loadLimit = 10;
  const limit = 2; // 用const替代let,避免意外修改

  async function fetchProducts() {
    if (loading) return; // 拦截重复请求
    const dataUrl = `https://dummyjson.com/products?limit=${limit}&skip=${products.length}`;

    try {
      setLoading(true);
      const response = await fetch(dataUrl);
      const result = await response.json();

      if (result?.products?.length) {
        setProducts(prev => [...prev, ...result.products]);
      } else {
        setDisableButton(true); // 无更多数据时禁用按钮
      }
    } catch (e) {
      console.error(e);
    } finally {
      setLoading(false); // 无论请求成败都关闭loading
    }
  }

  useEffect(() => {
    fetchProducts();
  }, []);

  useEffect(() => {
    if (products.length >= loadLimit) {
      setDisableButton(true);
    }
  }, [products]);

  if (loading) {
    return <div>Loading data! Please wait.</div>;
  }

  return (
    <Fragment>
      <div className='load-more-container'>
        <div className='product-container'>
          {products.map(item => (
            <div className='product' key={item.id}>
              <img src={item.thumbnail} alt={item.title} />
              <p>{item.title}</p>
              <span>$ {item.price}</span>
            </div>
          ))}
        </div>
        <div className='button-container'>
          <button disabled={disableButton || loading} onClick={fetchProducts}>
            Load more products
          </button>
          {disableButton && <p>You have reached {loadLimit} products!</p>}
        </div>
      </div>
    </Fragment>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 07:24:50