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;
问题原因
- 未缓存请求Promise:每次AbcProfile组件渲染时,都会创建新的fetch Promise并通过
use()执行,导致重复请求。 - 开发环境Strict Mode:Next.js默认开启的严格模式会在开发阶段触发组件双重渲染,进一步增加请求次数。
- 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
相关产品推荐
相关产品推荐

