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

如何优化加密货币收益计算器以避免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)有两个问题:

  1. setCoin(val)是异步的,此时coin还是旧值,应该直接用val
  2. 第一个参数传null没有对应处理逻辑,应该根据已有日期请求买入/卖出价格(参考防抖示例里的写法)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 10:16:04