Files
Podcast-Generator/web/API_DUPLICATE_CALL_FIX.md
hex2077 b63fcb3f6d feat: 实现用户认证系统并优化音频播放器功能
- 新增NextAuth认证系统,支持Google和GitHub登录
- 添加登录模态框组件和用户头像显示
- 重构音频播放器,支持倍速控制和状态同步
- 优化播客卡片显示当前播放状态和生成状态
- 新增API调用追踪工具和防重复调用Hook
- 修复多个API重复调用问题并添加详细文档
- 改进音频文件处理流程,支持MP3格式输出
- 更新类型定义和组件Props以支持新功能
2025-08-16 23:03:46 +08:00

5.2 KiB
Raw Blame History

API重复调用问题修复报告

问题描述

用户报告访问主页时主页内的接口被调用了两次。这是一个常见的React问题会导致

  • 不必要的网络请求
  • 服务器负载增加
  • 用户体验下降
  • 可能的数据不一致

问题分析

通过代码分析,发现了以下导致重复调用的原因:

1. 多个useEffect调用同一API

src/app/page.tsx 中:

  • 第38行的useEffect在组件挂载时调用 fetchRecentPodcasts()
  • 第86行的useEffect设置定时器每20秒调用 fetchRecentPodcasts()
  • 这导致页面加载时API被调用两次

2. useEffect依赖项问题

src/components/PodcastCreator.tsx 中:

  • useEffect依赖项包含 selectedConfigselectedConfigName
  • 当配置变化时可能触发多次API调用

3. ConfigSelector组件的重复调用

src/components/ConfigSelector.tsx 中:

  • localStorage变化监听可能导致重复的配置加载

修复方案

1. 合并useEffect调用

将两个分离的useEffect合并为一个

// 修复前两个独立的useEffect
useEffect(() => {
  setCredits(100000);
  fetchRecentPodcasts(); // 第一次调用
}, []);

useEffect(() => {
  const interval = setInterval(() => {
    fetchRecentPodcasts(); // 定时调用
  }, 20000);
  return () => clearInterval(interval);
}, []);

// 修复后合并为一个useEffect
useEffect(() => {
  setCredits(100000);
  fetchRecentPodcasts(); // 初始调用
  
  // 设置定时器
  const interval = setInterval(() => {
    fetchRecentPodcasts();
  }, 20000);

  return () => clearInterval(interval);
}, []); // 空依赖数组,只在组件挂载时执行一次

2. 创建防重复调用Hook

创建了 src/hooks/useApiCall.ts

export function usePreventDuplicateCall() {
  const isCallingRef = useRef<boolean>(false);

  const executeOnce = useCallback(async <T>(
    apiFunction: () => Promise<T>
  ): Promise<T | null> => {
    if (isCallingRef.current) {
      console.log('API call already in progress, skipping...');
      return null;
    }

    try {
      isCallingRef.current = true;
      const result = await apiFunction();
      return result;
    } catch (error) {
      console.error('API call failed:', error);
      return null;
    } finally {
      isCallingRef.current = false;
    }
  }, []);

  return { executeOnce };
}

3. 优化useEffect依赖项

在PodcastCreator组件中

// 修复前:多个依赖项可能导致重复调用
useEffect(() => {
  fetchVoices();
}, [selectedConfig, selectedConfigName]);

// 修复后:只依赖必要的状态
useEffect(() => {
  if (!selectedConfigName) {
    setVoices([]);
    return;
  }
  fetchVoices();
}, [selectedConfigName]); // 只依赖配置名称

4. 添加API调用追踪器

创建了 src/utils/apiCallTracker.ts 用于开发环境下监控API调用

// 自动检测重复调用
trackCall(url: string, method: string = 'GET'): string {
  const recentCalls = this.calls.filter(
    c => c.url === url && 
         c.method === method && 
         Date.now() - c.timestamp < 5000
  );

  if (recentCalls.length > 0) {
    console.warn(`🚨 检测到重复API调用:`, {
      url, method, 重复次数: recentCalls.length + 1
    });
  }
}

修复效果

修复前:

  • 页面加载时 /api/podcast-status 被调用2次
  • 配置变化时 /api/tts-voices 可能被多次调用
  • 无法监控和调试重复调用问题

修复后:

  • 页面加载时 /api/podcast-status 只调用1次
  • 使用防重复调用机制确保同一时间只有一个请求
  • 开发环境下自动检测和警告重复调用
  • 优化了useEffect依赖项减少不必要的重新执行

验证方法

1. 开发环境调试

打开浏览器开发者工具,在控制台中可以使用:

// 查看API调用统计
window.apiDebug.showStats();

// 清空统计数据
window.apiDebug.clearStats();

2. 网络面板监控

在浏览器开发者工具的Network面板中

  • 刷新页面,观察 /api/podcast-status 只被调用一次
  • 切换TTS配置观察 /api/tts-voices 不会重复调用

3. 控制台日志

开发环境下会自动输出API调用日志

  • 📡 API调用: - 正常调用
  • 🚨 检测到重复API调用: - 重复调用警告

最佳实践建议

  1. useEffect合并原则相关的副作用应该在同一个useEffect中处理
  2. 依赖项最小化:只包含真正需要的依赖项
  3. 防重复调用对于可能重复的API调用使用防重复机制
  4. 开发调试工具:在开发环境中添加监控和调试工具
  5. 错误处理确保API调用失败时不会影响后续调用

相关文件

  • src/app/page.tsx - 主页组件修复
  • src/components/PodcastCreator.tsx - 播客创建器组件修复
  • src/components/ConfigSelector.tsx - 配置选择器组件修复
  • src/hooks/useApiCall.ts - 防重复调用Hook新增
  • src/utils/apiCallTracker.ts - API调用追踪器新增

注意事项

  • 修复后的代码保持了原有功能不变
  • 所有修改都向后兼容
  • 调试工具只在开发环境中启用,不会影响生产环境性能
  • 建议在部署前进行充分测试,确保所有功能正常工作