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

React中如何阻止同组件二次渲染时重复发起API请求

解决多实例SelectDropdown重复API请求的方案

要实现全平台复用的SelectDropdown组件,同时避免多个组件实例重复发起相同API请求,可以通过Context统一管理请求状态、缓存数据和请求Promise的方式解决,具体实现如下:

核心思路

用Context共享三个关键状态:

  1. 已缓存的下拉选项数据
  2. 请求加载状态
  3. 正在进行的请求Promise引用

当第一个组件发起请求时,后续组件会复用这个正在进行的Promise,不会重复调用API;请求完成后,所有组件共享缓存数据。


具体实现步骤

1. 创建DropdownContext

定义Context来共享数据和方法,方便所有组件实例访问:

import { createContext, useContext, useState, useCallback } from 'react';

// 定义Context类型(可根据实际业务调整)
interface DropdownContextType {
  options: any[];
  isLoading: boolean;
  fetchOptions: () => Promise<void>;
}

// 初始化Context默认值
const DropdownContext = createContext<DropdownContextType>({
  options: [],
  isLoading: false,
  fetchOptions: async () => {},
});

// 自定义Hook简化Context调用
export const useDropdownContext = () => useContext(DropdownContext);

2. 实现Context Provider

在Provider内部管理请求逻辑,重点维护pendingPromise来拦截重复请求:

export const DropdownProvider = ({ children }: { children: React.ReactNode }) => {
  const [options, setOptions] = useState<any[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  let pendingPromise: Promise<void> | null = null; // 保存正在进行的请求Promise

  const fetchOptions = useCallback(async () => {
    // 已有缓存数据,直接返回
    if (options.length > 0) return;
    // 请求正在进行,复用已有Promise
    if (pendingPromise) return pendingPromise;

    setIsLoading(true);
    try {
      // 保存请求Promise,供后续组件复用
      pendingPromise = getSelections().then((items) => {
        setOptions(items);
      });
      await pendingPromise;
    } catch (error) {
      console.error('获取下拉选项失败:', error);
      setOptions([]); // 错误处理可根据业务调整
    } finally {
      setIsLoading(false);
      pendingPromise = null; // 请求完成后清空引用
    }
  }, [options]);

  return (
    <DropdownContext.Provider value={{ options, isLoading, fetchOptions }}>
      {children}
    </DropdownContext.Provider>
  );
};

3. 修改DropdownWithOptions组件

让组件从Context获取数据和方法,不再独立管理请求:

import { useEffect } from 'react';
import { useDropdownContext } from './DropdownContext';

const DropdownWithOptions = (props: any) => {
  const { options, isLoading, fetchOptions } = useDropdownContext();

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

  // 加载状态可根据业务自定义(比如骨架屏、加载文字)
  if (isLoading) {
    return <div>加载中...</div>;
  }

  return <Dropdown options={options} {...props} />;
};

4. 页面中使用Provider包裹组件

确保所有DropdownWithOptions实例处于同一个Context范围内:

import { DropdownProvider } from './DropdownContext';

const Page = () => {
  return (
    <DropdownProvider>
      <DropdownWithOptions />
      <DropdownWithOptions />
    </DropdownProvider>
  );
};

方案优势

  • 彻底解决多组件实例同时发起请求的问题:pendingPromise会拦截后续重复请求,让所有组件等待同一个请求完成
  • 数据共享:请求完成后所有组件直接复用缓存的options
  • 低侵入性:组件本身无需关心请求逻辑,只需从Context获取数据即可

额外优化建议

  • 如果需要支持不同类型的下拉选项(比如不同API接口),可以给fetchOptions添加key参数,在Provider中用对象维护多个pendingPromises,区分不同请求
  • 可添加缓存过期逻辑,比如记录数据更新时间戳,超过阈值后重新发起请求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 11:52:48