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

Next.js无需useEffect,Token过期时递归调用API的实现问题

解决Next.js中提交表单时token过期的重试问题

核心问题拆解

你当前面临两个关键问题:递归调用时的TypeScript类型不匹配,以及无限制重试导致无限循环的风险。根源在于直接复用FormEvent参数,且未对重试次数做限制。

解决方案步骤

1. 抽离核心提交逻辑,摆脱事件依赖

把API提交的核心逻辑从handleSubmit中拆分出来,写成独立函数,彻底避免对FormEvent的依赖,从根源解决类型问题:

// 抽离独立的提交函数,仅依赖token和产品数据
const submitProduct = async (currentAccessToken: string) => {
  const productData = {
    productName,
    brand,
    productDescription,
    stockQuantity,
    retailPrice,
    COGS,
    variants,
    warehouse_enter_date,
    exp_date,
    imageURL,
  };

  const response = await fetch("http://localhost:4000/dashboard/product/new", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${currentAccessToken}`,
    },
    body: JSON.stringify(productData),
  });

  if (!response.ok) {
    if (response.status === 403) {
      // 返回标记表示需要刷新token
      return { needsRefresh: true };
    }
    throw new Error("Failed to create product");
  }

  const responseData = await response.json();
  if (!responseData?.product?._id) {
    throw new Error("Invalid response data from server");
  }
  return { success: true, data: responseData };
};

2. 修改handleSubmit,加入重试控制和token刷新逻辑

在事件处理函数中限制重试次数(最多1次),避免refresh token失效时陷入无限循环,同时处理token刷新后的重新提交:

const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
  event.preventDefault();
  let retryCount = 0;
  const maxRetries = 1; // 限制最多重试1次,防止无限循环

  while (retryCount <= maxRetries) {
    try {
      const result = await submitProduct(accessToken);
      
      if (result.needsRefresh) {
        // 等待token刷新完成,必须加await
        await updateTokens(refreshToken);
        // 从localStorage读取新token(若用状态管理可直接取状态)
        const newAccessToken = localStorage.getItem("accessToken");
        if (!newAccessToken) throw new Error("Failed to get new access token");
        
        retryCount++;
        // 用新token重新提交
        const retryResult = await submitProduct(newAccessToken);
        if (retryResult.success) {
          router.push(`/dashboard/product/${retryResult.data.product._id}`);
          return;
        }
      } else if (result.success) {
        router.push(`/dashboard/product/${result.data.product._id}`);
        return;
      }
    } catch (error) {
      if (error instanceof Error) {
        console.error("Error creating product:", error.message);
      } else {
        console.error("Unknown error:", error);
      }
      break; // 出错直接终止,不再重试
    }
  }
};

3. 关键细节说明

  • 异步等待token刷新:必须给updateTokens加await,否则token还未更新完成就重新提交,会继续使用旧token导致403。
  • 重试次数限制:设置maxRetries为1,因为如果refresh token也失效,再重试毫无意义,直接终止避免无限循环。
  • 新token的获取:假设updateTokens会将新token存入localStorage,刷新后需重新读取;若使用状态管理(如useState、Redux),可直接同步读取状态,更高效。

额外优化建议

  • 将token存在React状态中,而非每次从localStorage读取,更新后状态自动同步,减少手动操作。
  • 封装全局请求拦截器(如用Axios),让所有API请求自动处理403刷新逻辑,不用每个提交函数重复编写。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 03:27:38