如何优化加密货币收益计算器以避免API调用次数超限?
解决加密货币收益计算器API调用限额问题
你的核心问题是输入变更时实时触发API请求,导致短时间内请求次数过多触发限额。下面是几种实用的解决方案,按优先级排序:
1. 给请求添加防抖(Debounce)
防抖能让请求在用户停止输入/操作后延迟一段时间再执行,避免连续输入时的重复请求。比如用户修改日期时,等他输入完(比如停顿500ms)再发请求,而不是每输入一个字符就发一次。
代码修改示例:
import { useState, useEffect, useCallback } from 'react'; export default function App() { // 原有state定义 const [trade, setTrade] = useState({ sellData: {}, buyData: {}, gains: 0 }); const coinList = [ { id: 0, name: "bitcoin" }, { id: 1, name: "ethereum" }, { id: 2, name: "tezos" }, { id: 3, name: "cardano" } ]; const [buyDate, setBuyDate] = useState(""); const [sellDate, setSellDate] = useState(""); const [volume, setVolume] = useState(0); const [coin, setCoin] = useState(""); // 防抖函数:延迟执行请求 const debounceFetch = useCallback((func, delay) => { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => func.apply(this, args), delay); }; }, []); const coingeckoUrl = (coin, date) => { return `https://api.coingecko.com/api/v3/coins/${coin}/history?date=${date}&localization=false`; }; const coingeckoFetch = async (buy, coin, date) => { fetch(coingeckoUrl(coin, date)).then((response) => response.json().then((jsonData) => { if (buy) { setTrade({ ...trade, buyData: jsonData }); } else { setTrade({ ...trade, sellData: jsonData }); } }) ); }; // 包装后的防抖请求函数 const debouncedCoingeckoFetch = useCallback(debounceFetch(coingeckoFetch, 500), [coingeckoFetch]); // 修改事件处理器,使用防抖后的请求 const handleBuyChange = (e) => { const val = e.target.value; setBuyDate(val); if (coin) { // 确保币种已选择再发请求 debouncedCoingeckoFetch(true, coin, val); } }; const handleSellChange = (e) => { const val = e.target.value; setSellDate(val); if (coin) { debouncedCoingeckoFetch(false, coin, val); } }; // 修复handleCoinChange的异步state问题 const handleCoinChange = (e) => { const val = e.target.value; setCoin(val); // 用新选中的币种,同步更新已有日期的价格 if (buyDate) debouncedCoingeckoFetch(true, val, buyDate); if (sellDate) debouncedCoingeckoFetch(false, val, sellDate); }; const calcGains = () => { setTrade({ ...trade, gains: (trade.sellData.market_data?.current_price.usd - trade.buyData.market_data?.current_price.usd) * volume }); }; // 原有渲染逻辑 return ( <div className="App"> <select defaultValue={coin} onChange={(val) => handleCoinChange(val)}> {coinList.map((item) => ( <option key={item.id}>{item.name}</option> ))} </select> <input placeholder="Insert Buy Date" defaultValue={buyDate} onChange={(val) => handleBuyChange(val)} /> <h3> {trade.buyData.market_data?.current_price.usd} USD</h3> <input placeholder="Insert Sell Date" defaultValue={sellDate} onChange={(val) => handleSellChange(val)} /> <h3> {trade.sellData.market_data?.current_price.usd} USD</h3> <input placeholder="Insert Amount of Tokens" value={volume} onChange={(e) => setVolume(e.target.value)} /> <h3>{volume}</h3> <button onClick={calcGains}> Calculate </button> <h3>{trade.gains} USD</h3> </div> ); }
2. 缓存请求结果
相同币种+日期的请求不需要重复发送,把结果存在本地缓存里,下次直接取缓存,减少API调用次数。
代码修改示例:
export default function App() { const [trade, setTrade] = useState({ sellData: {}, buyData: {}, gains: 0 }); // 添加缓存state,格式为 { "coin-date": 响应数据 } const [priceCache, setPriceCache] = useState({}); // 其他state和函数定义... const coingeckoFetch = async (buy, coin, date) => { const cacheKey = `${coin}-${date}`; // 先查缓存,存在就直接用,不用发请求 if (priceCache[cacheKey]) { const jsonData = priceCache[cacheKey]; if (buy) { setTrade({ ...trade, buyData: jsonData }); } else { setTrade({ ...trade, sellData: jsonData }); } return; } // 缓存不存在才发请求 fetch(coingeckoUrl(coin, date)) .then((response) => response.json()) .then((jsonData) => { // 更新缓存 setPriceCache(prev => ({ ...prev, [cacheKey]: jsonData })); if (buy) { setTrade({ ...trade, buyData: jsonData }); } else { setTrade({ ...trade, sellData: jsonData }); } }); }; // 其他渲染逻辑... }
3. 调整请求触发时机
不要实时触发请求,而是让用户手动触发——比如添加「获取价格」按钮,或者在点击「Calculate」时才一次性获取买入/卖出价格(如果还没获取的话)。
代码修改示例(点击计算时统一请求):
const calcGains = async () => { // 先检查是否已有买入/卖出数据,没有则请求 let updatedBuyData = trade.buyData; let updatedSellData = trade.sellData; if (!updatedBuyData.market_data && coin && buyDate) { const buyResponse = await fetch(coingeckoUrl(coin, buyDate)); updatedBuyData = await buyResponse.json(); } if (!updatedSellData.market_data && coin && sellDate) { const sellResponse = await fetch(coingeckoUrl(coin, sellDate)); updatedSellData = await sellResponse.json(); } // 更新trade数据并计算收益 const newGains = (updatedSellData.market_data?.current_price.usd - updatedBuyData.market_data?.current_price.usd) * volume; setTrade({ buyData: updatedBuyData, sellData: updatedSellData, gains: newGains }); }; // 移除原handleBuyChange、handleSellChange里的请求逻辑,只保留更新state const handleBuyChange = (e) => { setBuyDate(e.target.value); }; const handleSellChange = (e) => { setSellDate(e.target.value); };
额外修复:原代码的bug
你的handleCoinChange里调用coingeckoFetch(null, coin, val)有两个问题:
setCoin(val)是异步的,此时coin还是旧值,应该直接用val- 第一个参数传
null没有对应处理逻辑,应该根据已有日期请求买入/卖出价格(参考防抖示例里的写法)
内容的提问来源于stack exchange,提问作者NewCoder19
相关产品推荐
相关产品推荐

