React 18.3.0使用use钩子结合Suspense出现多次重渲染问题求助
问题描述
使用React 18.3.0-canary-3ff846d10-20230724 + Next.js 13.4.12,尝试通过React的use钩子结合Suspense实现数据请求时,遇到组件多次重渲染问题:控制台反复输出“render SuspenseTest”(约5至8次),直到加载完成显示“Done”。预期组件仅渲染一次,已尝试React.memo、useMemo、useCallback优化但无效,希望不依赖外部库解决该问题。
相关代码:
// src/pages/index.tsx import { Suspense } from "react" import Head from "next/head" import SuspenseTest from "@/components/modules/SuspenseTest" const Top = (): JSX.Element => { return ( <> <Head> <title>Suspense Test</title> </Head> <main> <Suspense fallback={<div>Now Loading...</div>}> <SuspenseTest /> </Suspense> </main> </> ) } export default Top
// src/components/modules/SuspenseTest/index.tsx import { use } from "react" const fetcher = (): Promise<string> => { return new Promise((resolve) => setTimeout(() => { resolve("Done") }, 5000), ) // 实际业务中的请求逻辑 // return axiosClient // .get("/articles") // .then((response) => { // return response.data as Promise<Article[]> // }) // .catch((error) => console.error(error)) } const SuspenseTest = (): JSX.Element => { console.log("render SuspenseTest") const message = use<string>(fetcher()) return <div>{message}</div> } export default SuspenseTest
解决方案
核心问题是每次组件渲染时都会创建新的Promise实例,触发Suspense反复挂起、恢复,导致多次重渲染。解决关键是稳定Promise实例,确保组件每次渲染使用同一个Promise。
方法1:用useMemo缓存Promise
通过useMemo包裹Promise创建逻辑,依赖项设为空数组,确保仅在组件首次渲染时创建一次Promise:
// src/components/modules/SuspenseTest/index.tsx import { use, useMemo } from "react" const fetcher = (): Promise<string> => { return new Promise((resolve) => setTimeout(() => { resolve("Done") }, 5000), ) } const SuspenseTest = (): JSX.Element => { console.log("render SuspenseTest") // 缓存Promise实例,避免每次渲染重复创建 const dataPromise = useMemo(() => fetcher(), []) const message = use<string>(dataPromise) return <div>{message}</div> } export default SuspenseTest
方法2:将Promise提升到组件外部(静态创建)
如果请求不需要依赖组件的props或state,直接在组件外部定义Promise,整个应用生命周期内仅创建一次:
// src/components/modules/SuspenseTest/index.tsx import { use } from "react" // 组件外部静态创建Promise,仅执行一次 const dataPromise = new Promise<string>((resolve) => setTimeout(() => { resolve("Done") }, 5000), ) const SuspenseTest = (): JSX.Element => { console.log("render SuspenseTest") const message = use<string>(dataPromise) return <div>{message}</div> } export default SuspenseTest
为什么之前的优化无效?
React.memo、useMemo、useCallback的优化前提是依赖项稳定,但你之前每次渲染都生成新的Promise,导致Suspense状态反复变化——即使组件被memo包裹,内部依赖的Promise实例变更仍会触发重渲染和Suspense的挂起/恢复流程。
内容的提问来源于stack exchange,提问作者Isuke FUJIYAMA
相关产品推荐
相关产品推荐

