如何解决NextJS中Recoil结合React Suspense的504网关超时问题
在NextJS中正确使用Recoil的useRecoilValue解决异步请求问题
针对你遇到的警告和线上504超时问题,下面提供两种可行的解决方案,都是在NextJS环境下验证过的:
方案一:提前在服务端获取数据并注入Recoil状态
核心思路是把异步请求从Recoil selector移到NextJS的页面数据获取方法中,避免组件渲染时发起异步操作,同时保证服务端与客户端状态同步。
- 修改Recoil状态定义,将异步selector改为普通atom:
// selector.js import { atom } from "recoil"; export const siteStateAtom = atom({ key: "siteState", default: { contactEmail: "", internationalTelephone: "" } });
- 在页面组件中通过
getServerSideProps(或getStaticProps)提前获取数据:
// pages/article.js(假设ExampleBlock是该页面的子组件) import { gql } from "@apollo/client"; import client from "../apollo-client"; import { RecoilRoot } from "recoil"; import { siteStateAtom } from "@recoil/selector"; import ArticleQuoteBlock from "../components/ExampleBlock"; export async function getServerSideProps() { const response = await client.query({ query: gql` { siteSettings { siteSettings { contactEmail internationalTelephone } } } `, }); const siteState = { contactEmail: response.data.siteSettings.siteSettings.contactEmail, internationalTelephone: response.data.siteSettings.siteSettings.internationalTelephone, }; return { props: { siteState } }; } export default function ArticlePage({ siteState }) { return ( <RecoilRoot initializeState={({ set }) => set(siteStateAtom, siteState)}> <ArticleQuoteBlock data={{ text: "示例文本", name: "示例名称" }} /> </RecoilRoot> ); }
- 更新子组件直接读取同步atom:
// ExampleBlock.js import React from "react"; import { useRecoilValue } from "recoil"; import { siteStateAtom } from "@recoil/selector"; import tw from "twin.macro"; export default function ArticleQuoteBlock({ data: { text, name } }) { const { contactEmail, internationalTelephone } = useRecoilValue(siteStateAtom); return ( <section tw="bg-white quote md:quote-md"> <div tw="bg-pink quote-inner md:quote-inner-md"> <h1 tw="quote-text md:quote-text-md">{contactEmail}</h1> <h1 tw="quote-text md:quote-text-md">{internationalTelephone}</h1> {text && ( <p tw="quote-text md:quote-text-md indent md:indent-md">{text}</p> )} {name && <p tw="name md:name-md">{name}</p>} </div> </section> ); }
方案二:使用useRecoilLoadable手动处理异步状态
如果不想改动数据获取逻辑,可以用Recoil提供的useRecoilLoadable替代useRecoilValue,手动管理加载、成功、失败状态,无需依赖React Suspense。
修改ExampleBlock.js:
import React from "react"; import { useRecoilLoadable } from "recoil"; import { stateQuery } from "@recoil/selector"; import tw from "twin.macro"; export default function ArticleQuoteBlock({ data: { text, name } }) { const loadable = useRecoilLoadable(stateQuery); switch (loadable.state) { case "hasValue": const { contactEmail, internationalTelephone } = loadable.contents; return ( <section tw="bg-white quote md:quote-md"> <div tw="bg-pink quote-inner md:quote-inner-md"> <h1 tw="quote-text md:quote-text-md">{contactEmail}</h1> <h1 tw="quote-text md:quote-text-md">{internationalTelephone}</h1> {text && ( <p tw="quote-text md:quote-text-md indent md:indent-md">{text}</p> )} {name && <p tw="name md:name-md">{name}</p>} </div> </section> ); case "loading": return <div>加载中...</div>; case "hasError": return <div>加载失败:{loadable.contents.message}</div>; } }
问题根源说明
你之前遇到的问题,本质是NextJS SSR流程与Recoil异步selector的Suspense处理不兼容:本地环境请求快不会触发超时,但线上网络环境下,服务端渲染时Recoil的Suspense挂起会导致请求超时。上述两种方案分别从"提前获取数据"和"手动管理异步状态"两个角度规避了这个冲突。
内容的提问来源于stack exchange,提问作者steven
相关产品推荐
相关产品推荐

