React汇率转换应用API多次调用致429错误排查求助
解决React汇率转换应用中API重复调用与多次渲染问题
问题根源分析
API重复调用原因
- React Strict Mode 双重执行:开发环境下Strict Mode会主动双重执行副作用函数(如
useEffect),导致fetchCurrency被触发两次。 - 无请求拦截机制:Store中的
fetchCurrency函数没有判断请求状态,每次调用都会直接发起新请求,即便数据已经存在。
多次console.log原因
- 组件重渲染触发:组件初始渲染、Store状态更新、Strict Mode双重执行都会引发组件重渲染,每次重渲染都会执行
console.log。 - 多余状态同步:额外使用
code这个useState同步Store数据,导致每次Store更新都会触发setCode,进而引发额外重渲染。
解决方案
1. 阻止API重复调用
方法一:在Store中添加请求状态控制
通过新增isLoading和hasFetched状态,确保仅在未请求、未加载时发起API调用:
import { create } from "zustand"; import { devtools } from "zustand/middleware"; import { getCurrencyCode } from "./services/CurrencyExchangeService"; import { CurrencyCode } from "./types"; type CurrencyExchangeStore={ currencyCode : CurrencyCode[] isLoading: boolean hasFetched: boolean fetchCurrency: () => Promise<void> } export const useCurrencyExchangeStore = create<CurrencyExchangeStore>()(devtools((set, get)=>({ currencyCode: [] as CurrencyCode[], isLoading: false, hasFetched: false, fetchCurrency: async ()=>{ const { isLoading, hasFetched } = get(); // 已加载或正在请求时直接返回 if (isLoading || hasFetched) return; set({ isLoading: true }); try { const currencyCode = await getCurrencyCode(); set(()=>({ currencyCode, hasFetched: true })); } catch (err) { console.error('获取货币代码失败:', err); } finally { set({ isLoading: false }); } } })))
方法二:给API请求添加缓存
在请求函数中加入内存/本地存储缓存,避免重复请求:
import axios from "axios"; import { CurrencyCodesSchema } from "../schema/currency-exchange-schema"; import { CurrencyCode } from "../types"; const appId= import.meta.env.VITE_API_KEY // 内存缓存 let currencyCache: CurrencyCode[] | null = null; export const getCurrencyCode = async()=>{ // 优先返回缓存数据 if (currencyCache) return currencyCache; // 尝试读取本地存储的持久化缓存 const localCache = localStorage.getItem('currencyCodes'); if (localCache) { currencyCache = JSON.parse(localCache); return currencyCache; } const url = `https://api.currencyapi.com/v3/currencies?apikey=${appId}¤cies=` const {data: {data}} = await axios(url) const arrayCurrency = CurrencyCodesSchema.safeParse(Object.entries(data).map(([code, currency])=>{ return{ id : code, data : currency } })) if(arrayCurrency.success){ currencyCache = arrayCurrency.data; localStorage.setItem('currencyCodes', JSON.stringify(arrayCurrency.data)); return arrayCurrency.data } }
2. 减少不必要的重渲染与日志输出
移除多余的状态同步
直接从Store读取currencyCode,无需额外useState同步,减少重渲染触发点:
import { useEffect } from "react" import { useCurrencyExchangeStore } from "../store" const Form = () => { const { currencyCode, fetchCurrency } = useCurrencyExchangeStore() useEffect(()=>{fetchCurrency()},[]) // 仅在数据更新时打印,避免无意义的空数组日志 useEffect(() => { if (currencyCode.length > 0) { console.log(currencyCode); } }, [currencyCode]) return ( <div>Form</div> ) } export default Form
关于Strict Mode双重渲染
开发环境下的双重执行是React的调试机制,生产环境会自动消失,不建议长期关闭该模式,它能帮助提前发现组件的潜在问题。
内容的提问来源于stack exchange,提问作者Sachin Tekchandani
相关产品推荐
相关产品推荐

