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

Next.js客户端组件Suspense异常:API重复调用4次问题排查

在Next.js中正确使用客户端组件的Suspense并避免API重复调用

问题描述

在Server Component中加载Client Component,期望通过Suspense的fallback展示加载占位符,直到Client Component内的AbcProfile组件通过API获取数据。运行代码时虽能显示fallback,但API被重复调用4次,需要解决这个问题并正确实现Suspense逻辑。

现有代码

Server Component(MyProfile)

import { LoggedInUserModel } from "@/models/common/logged-in-user-model";
import { Suspense } from "react";
import dynamic from "next/dynamic";
import ProfileLoading from "./loading22";
const UserProfile = dynamic(() => import("./components/userProfile"), {
  ssr: false,
});

const MyProfile = async () => {
 const user: LoggedInUserModel = await userService.getUser();
  return (
    <>
      <div className="row mt-3 justify-content-md-center">
        <div className="col-12 col-md-8">
          <nav
            aria-label="breadcrumb"
            className="p-3 bg-white rounded-3 shadow-sm mb-4"
          >
            <ol className="breadcrumb mb-0">
              <li className="breadcrumb-item">
                <a className="text-decoration-none">
                  <i className="fa-solid fa-user"></i> My Profile
                </a>
              </li>
            </ol>
          </nav>
        </div>

        <UserProfile user={user} />

      </div>
    </>
  );
};

export default MyProfile;

Client Component(UserProfile)

"use client";
import { UserProfileModel } from "./../../../../models/User/user-profile-model";
import { Suspense, lazy, use, useEffect, useState } from "react";
import fetchClientData from "@/lib/fetchClientData";
import { LoggedInUserModel } from "@/models/common/logged-in-user-model";
import ProfileLoading from "../loading22";
import React from "react";
const AbcProfile = lazy(() => import("./abcprofile"));

type UserProfileProps = {
  user: LoggedInUserModel;
};
const UserProfile = ({ user }: UserProfileProps) => {
 

  return (
    <Suspense fallback={<h2>Data loading...........</h2>}>
      <AbcProfile />
    </Suspense>
  );
};

export default UserProfile;

AbcProfile组件

import { use, useEffect, useState } from "react";

const AbcProfile = () => {
  use(
    fetch(`/api/user/profile/6490986e0d0ce1756d2b491b`).then((res) =>
      res.json()
    )
  );

  return <h2>Data loaded</h2>;
};

export default AbcProfile;

问题原因

  1. 未缓存请求Promise:每次AbcProfile组件渲染时,都会创建新的fetch Promise并通过use()执行,导致重复请求。
  2. 开发环境Strict Mode:Next.js默认开启的严格模式会在开发阶段触发组件双重渲染,进一步增加请求次数。
  3. Lazy组件加载的额外渲染:配合lazy加载时,组件初始化过程可能触发额外渲染周期,叠加后导致四次请求。

解决方案

1. 缓存API请求Promise

在AbcProfile组件中使用useMemo缓存fetch请求的Promise,确保无论组件渲染多少次,请求只会发起一次:

import { use, useMemo } from "react";

const AbcProfile = () => {
  // 用useMemo缓存Promise,空依赖数组确保只创建一次
  const profilePromise = useMemo(() => {
    return fetch(`/api/user/profile/6490986e0d0ce1756d2b491b`).then(res => res.json());
  }, []);

  use(profilePromise);

  return <h2>Data loaded</h2>;
};

export default AbcProfile;

2. (可选)关闭开发环境Strict Mode

如果仅在开发环境出现重复请求,生产环境正常,可临时关闭Strict Mode验证。在next.config.js中修改:

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: false,
};

module.exports = nextConfig;

注意:不建议长期关闭Strict Mode,它能帮助提前发现组件潜在问题。

3. 优化组件结构(可选)

如果不需要lazy加载AbcProfile,可以直接导入组件,减少lazy加载带来的额外渲染周期:
修改UserProfile组件:

"use client";
import { UserProfileModel } from "./../../../../models/User/user-profile-model";
import { Suspense } from "react";
import { LoggedInUserModel } from "@/models/common/logged-in-user-model";
import AbcProfile from "./abcprofile";

type UserProfileProps = {
  user: LoggedInUserModel;
};
const UserProfile = ({ user }: UserProfileProps) => {
  return (
    <Suspense fallback={<h2>Data loading...........</h2>}>
      <AbcProfile />
    </Suspense>
  );
};

export default UserProfile;

验证效果

修改后,开发环境下API请求次数会减少到2次(Strict Mode导致),生产环境下只会发起1次请求,Suspense的fallback也能正常展示直到数据加载完成。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 14:15:59