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

React应用中正确调用API及规避429请求过多错误方案

问题解答

1. 当前请求写法是否合理?

当前写法不合理。核心问题在于:

  • 组件每次挂载(比如路由切换回该页面、父组件重渲染导致ListTour重新挂载)都会触发空依赖的useEffect,重复发起API请求;
  • 完全没有缓存机制,多次访问同一页面会频繁触发请求,很容易触发服务端的请求频率限制,也就是你遇到的429错误。

2. 实现首次请求+缓存的几种可行方案

方案一:组件内本地缓存(适合单组件复用场景)

用useRef存储缓存数据(不会随组件渲染重置),组件挂载时先检查缓存,有数据直接复用,没有再发起请求。缺点是组件销毁后缓存会丢失,比如路由切换离开再回来会重新请求。

修改后的组件代码:

import React, { useEffect, useState, useRef } from 'react';
import { Link } from "react-router-dom";
import classes from "./listTour.module.css";
import { URL } from '../../../URL'
import axios from "axios";

const ListTour = (props) => {
    const [cardTour, setCardTour] = useState([]);
    // 用useRef存缓存,避免组件渲染时重置
    const tourCache = useRef(null);

    useEffect(() => {
        const getListTournaments = async () => {
            // 先读缓存,有数据直接用
            if (tourCache.current) {
                setCardTour(tourCache.current);
                return;
            }
            try {
                const tours = await axios.get(`${URL}/tournaments`)
                console.log(tours.data)
                // 写入缓存
                tourCache.current = tours.data;
                setCardTour(tours.data)
            } catch (error) {
                console.log(error)
            }
        }
        getListTournaments()
    }, [])

    return (
        cardTour.map(tour => {
            return (
                <Link key={tour.id} to={`Profile/${tour.name_tour_sort}`}>
                    <div className={classes.cardTour}>
                        <div className={classes.cardTourAdaptive}>
                            <div className={classes.cardTourBanner}>
                                <img className={classes.cardTourBannerImg}
                                     src={`/images/LogoAndBannerTour/${tour.banner_tour}`} alt={`${tour.name_tour}`}/>
                            </div>
                            <div className={classes.nameTour}>{tour.name_tour}</div>
                            <div className={classes.prizePoolTour}>{tour.prize_pool}</div>
                            <span className={classes.DateTour}>{tour.date_tour}</span>
                        </div>
                    </div>
                </Link>
            )
        })
    );
};

export default ListTour;

方案二:全局缓存(适合跨组件/路由共享数据)

如果需要在整个应用的不同页面复用赛事数据,路由切换后也保留缓存,可以单独写一个缓存管理模块:

  1. 创建tournamentCache.js缓存模块:
// 全局存储缓存数据
let tournamentData = null;

export const getTournaments = async (axiosInstance, baseUrl) => {
    // 有缓存直接返回
    if (tournamentData) {
        return tournamentData;
    }
    // 无缓存则发起请求并写入缓存
    const res = await axiosInstance.get(`${baseUrl}/tournaments`);
    tournamentData = res.data;
    return tournamentData;
};

// 可选:提供清除缓存的方法,用于数据更新时手动刷新
export const clearTournamentCache = () => {
    tournamentData = null;
};
  1. 在组件中使用该模块:
import React, { useEffect, useState } from 'react';
import { Link } from "react-router-dom";
import classes from "./listTour.module.css";
import { URL } from '../../../URL'
import axios from "axios";
import { getTournaments } from '../path/to/tournamentCache';

const ListTour = (props) => {
    const [cardTour, setCardTour] = useState([]);

    useEffect(() => {
        const fetchData = async () => {
            try {
                const tours = await getTournaments(axios, URL);
                setCardTour(tours);
            } catch (error) {
                console.log(error)
            }
        }
        fetchData()
    }, [])

    // 渲染部分和原代码一致...
};

export default ListTour;

这种方式的缓存全局有效,只要应用不刷新,任何组件调用getTournaments都会复用已有数据。

方案三:Axios拦截器统一处理缓存

通过Axios的请求/响应拦截器,对指定接口自动做缓存,不需要在组件中单独处理:

  1. 初始化带拦截器的Axios实例:
import axios from 'axios';
import { URL } from './URL';

const axiosInstance = axios.create({ baseURL: URL });

// 缓存存储容器
const requestCache = new Map();

// 请求拦截器:检查缓存,有则直接返回缓存数据
axiosInstance.interceptors.request.use(config => {
    // 只对GET请求的tournaments接口做缓存
    if (config.method === 'get' && config.url === '/tournaments') {
        const cachedData = requestCache.get('/tournaments');
        if (cachedData) {
            // 直接返回缓存的响应,跳过实际请求
            return Promise.resolve({ data: cachedData });
        }
    }
    return config;
});

// 响应拦截器:成功响应后写入缓存
axiosInstance.interceptors.response.use(response => {
    if (response.config.method === 'get' && response.config.url === '/tournaments') {
        requestCache.set('/tournaments', response.data);
    }
    return response;
});

export default axiosInstance;
  1. 组件中使用该Axios实例:
// 导入自定义的Axios实例
import axiosInstance from '../path/to/axiosInstance';

// 修改useEffect中的请求代码
const tours = await axiosInstance.get('/tournaments')

这种方式可以统一管理多个接口的缓存,适合多组件调用同一接口的场景。

补充提示

如果赛事数据会定期更新,可以给缓存添加过期时间,或者在数据更新后调用清除缓存的方法,确保用户能看到最新数据。另外,429错误除了缓存,也可以搭配请求重试机制,但缓存是从根源减少请求的最优解。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 08:11:27