# API重复调用问题修复报告 ## 问题描述 用户报告访问主页时,主页内的接口被调用了两次。这是一个常见的React问题,会导致: - 不必要的网络请求 - 服务器负载增加 - 用户体验下降 - 可能的数据不一致 ## 问题分析 通过代码分析,发现了以下导致重复调用的原因: ### 1. **多个useEffect调用同一API** 在 `src/app/page.tsx` 中: - 第38行的useEffect在组件挂载时调用 `fetchRecentPodcasts()` - 第86行的useEffect设置定时器每20秒调用 `fetchRecentPodcasts()` - 这导致页面加载时API被调用两次 ### 2. **useEffect依赖项问题** 在 `src/components/PodcastCreator.tsx` 中: - useEffect依赖项包含 `selectedConfig` 和 `selectedConfigName` - 当配置变化时可能触发多次API调用 ### 3. **ConfigSelector组件的重复调用** 在 `src/components/ConfigSelector.tsx` 中: - localStorage变化监听可能导致重复的配置加载 ## 修复方案 ### 1. **合并useEffect调用** 将两个分离的useEffect合并为一个: ```typescript // 修复前:两个独立的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`: ```typescript export function usePreventDuplicateCall() { const isCallingRef = useRef(false); const executeOnce = useCallback(async ( apiFunction: () => Promise ): Promise => { 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组件中: ```typescript // 修复前:多个依赖项可能导致重复调用 useEffect(() => { fetchVoices(); }, [selectedConfig, selectedConfigName]); // 修复后:只依赖必要的状态 useEffect(() => { if (!selectedConfigName) { setVoices([]); return; } fetchVoices(); }, [selectedConfigName]); // 只依赖配置名称 ``` ### 4. **添加API调用追踪器** 创建了 `src/utils/apiCallTracker.ts` 用于开发环境下监控API调用: ```typescript // 自动检测重复调用 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. **开发环境调试** 打开浏览器开发者工具,在控制台中可以使用: ```javascript // 查看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调用追踪器(新增) ## 注意事项 - 修复后的代码保持了原有功能不变 - 所有修改都向后兼容 - 调试工具只在开发环境中启用,不会影响生产环境性能 - 建议在部署前进行充分测试,确保所有功能正常工作