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

React路由切换时如何保存组件状态避免重复发起API请求

问题核心原因

路由切换时ProductList组件被完整卸载,组件内部state会被全部回收,所以每次返回都会重新执行初始化逻辑触发请求。你之前用的localStorage方案偶发失效,基本是这几个问题导致的:

  • 存储时机不对:把存数据的逻辑放在卸载阶段,如果跳转时接口请求还没完成,存进去的就是初始空数组
  • 缺少校验:没有判断缓存是否有效、是否过期,容易拿到格式错误或者过旧的数据
  • 没有拦截请求:读完缓存之后还是执行了后续请求逻辑,等于没做缓存
可落地实现方案

按改造成本从低到高排序,选一个适合你项目的就行:

方案1:修复localStorage缓存逻辑(零额外依赖)

不用改现有项目结构,只要补全缓存读写的校验逻辑,不要等卸载时再存数据,每次拿到新数据立刻同步缓存即可,参考修改后的代码:

import React, { useState, useEffect} from "react";
import ProductItem from "../Components/ProductItem";
import axios from "axios";

// 缓存配置
const CACHE_KEY = "hotel_list_cache";
const CACHE_TTL = 10 * 60 * 1000; // 缓存有效期10分钟,可自行调整

const ProductList = () => {
  // 初始化时优先读有效缓存
  const [hotelList, setHotelList] = useState(() => {
    const cacheStr = localStorage.getItem(CACHE_KEY);
    if (cacheStr) {
      try {
        const cache = JSON.parse(cacheStr);
        // 缓存没过期就直接用
        if (cache.expireTime > Date.now()) {
          return cache.data;
        }
      } catch (err) {
        // 缓存格式错误直接清掉
        localStorage.removeItem(CACHE_KEY);
      }
    }
    return [];
  });

  // 统一更新列表和缓存,不用等卸载再存
  const updateHotelList = (list) => {
    setHotelList(list);
    localStorage.setItem(CACHE_KEY, JSON.stringify({
      data: list,
      expireTime: Date.now() + CACHE_TTL
    }));
  };

  // Get user location by IP
  const getCurrentLocation = () => {
    return fetch("https://ipinfo.io/json?token=MyToken").then(
      (response) => response.json()
    );
  };

  // Get list of hotels in specific location
  const getHotelsInLocation = (destInfo) => {
    console.log('destInfo is: ', destInfo)
    const options = {
      method: "GET",
      url: "https://booking-com.p.rapidapi.com/v1/hotels/search",
      params: {
        checkout_date: "2022-10-01",
        units: "metric",
        dest_id: destInfo.destId,
        dest_type: destInfo.destType,
        locale: "en-gb",
        adults_number: 2,
        order_by: "popularity",
        filter_by_currency: "USD",
        checkin_date: "2022-09-30",
        room_number: 1,
      },
      headers: {
        "X-RapidAPI-Host": "booking-com.p.rapidapi.com",
        "X-RapidAPI-Key": "MyApiKey",
      },
    };
    axios
      .request(options)
      .then(function (response) {
        console.log(response.data.result);
        updateHotelList(response.data.result);
      })
      .catch(function (error) {
        console.error(error);
      });
  };

  useEffect(() => {
    // 已有有效缓存直接跳过所有请求
    if (hotelList.length > 0) return;

    getCurrentLocation().then((currentLocation) => {
      console.log("Current city ", currentLocation.city);
      const options = {
        method: "GET",
        url: "https://booking-com.p.rapidapi.com/v1/hotels/locations",
        params: { locale: "en-gb", name: currentLocation.city },
        headers: {
          "X-RapidAPI-Host": "booking-com.p.rapidapi.com",
          "X-RapidAPI-Key": "MyApiKey",
        },
      };
      axios
        .request(options)
        .then(function (response) {
          console.log(response.data);
          let destId = response.data[0].dest_id;
          let destType = response.data[0].dest_type;
          const destInfo = { destId, destType };
          getHotelsInLocation(destInfo);
        })
        .catch(function (error) {
          console.error(error);
        });
    });
  }, [hotelList.length]);

  return (
    <>
      {hotelList.map((hotel) => (
        <ProductItem key={hotel.hotel_id} hotel={hotel} />
      ))}
    </>
  );
};

export default ProductList;

这个方案适合小型项目,缺点是localStorage在隐私模式下可能被禁用,刷新页面缓存虽然还在,但如果用户清了浏览器存储就会重新请求。

方案2:状态上提(无兼容问题)

如果你的路由外层有不会随路由切换卸载的布局组件,直接把hotelList相关的state和请求逻辑挪到这个父组件上,通过props或者Context传给ProductList即可。只要父组件不销毁,状态就会一直保留,不需要做本地存储,也没有序列化、缓存失效的坑,适合路由结构简单的项目。

方案3:使用专业请求缓存库(生产环境推荐)

如果项目里有大量类似的列表缓存需求,直接用SWR、React Query这类成熟的请求库,自带内存缓存机制,只要配置好缓存有效期,同一个请求在有效期内不会重复发起,组件卸载再重新挂载时会直接从内存里取之前的返回数据,不需要自己写任何缓存读写逻辑,还能自动处理请求竞态、重试、后台刷新等问题,稳定性比自己手写缓存高很多。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 03:27:25