如何在Next.js的_app中仅调用一次API并将数据传递给所需子组件
Next.js 全局顶层数据共享解决方案
针对你遇到的场景,以下是几个可落地的最优方案,可根据你的Next.js版本选择:
方案1:React Context + 全局单次请求(适配Pages Router/App Router全版本)
这是兼容性最高的方案,可实现仅调用一次API全应用共享数据:
- 首先创建全局Context:
// contexts/GlobalDataContext.js import { createContext, useContext, useEffect, useState } from 'react'; const GlobalDataContext = createContext(); export function GlobalDataProvider({ children }) { const [navData, setNavData] = useState(null); useEffect(() => { // 全局仅发起一次API请求 fetch('/your/api/endpoint') .then(res => res.json()) .then(data => setNavData(data)); }, []); return ( <GlobalDataContext.Provider value={{ navData }}> {children} </GlobalDataContext.Provider> ); } export function useGlobalData() { return useContext(GlobalDataContext); }
- 在
_app.js中包裹Provider,你的全局Layout不用改位置,保持全局引入即可:
// pages/_app.js import { GlobalDataProvider } from '../contexts/GlobalDataContext'; import Layout from './layout'; export default function MyApp({ Component, pageProps }) { return ( <GlobalDataProvider> <Layout> <Component {...pageProps} /> </Layout> </GlobalDataProvider> ); }
- 导航栏组件直接调用自定义Hook拿数据即可,不用逐层传props:
// components/Navbar.js import { useGlobalData } from '../contexts/GlobalDataContext'; export default function Navbar() { const { navData } = useGlobalData(); // 渲染搜索表单逻辑 }
如果需要服务端预取数据避免客户端请求的加载闪烁,可以在_app.js中添加getInitialProps全局请求一次,拿到数据后通过pageProps传入Context即可,_app.js本身是支持getInitialProps调用API的,不存在禁止请求的限制。
方案2:根Layout直接请求(适配Next.js 13+ App Router)
如果你已经升级到App Router版本,实现逻辑更简单:
- 根
app/layout.js本身是服务端组件,支持直接顶层await发起API请求,数据请求仅会在应用初始化时调用一次 - 拿到数据后直接传递给Navbar组件即可,所有嵌套页面都会共享这个根布局的导航栏数据
// app/layout.js import Navbar from './components/Navbar'; import Footer from './components/Footer'; async function getNavData() { const res = await fetch('your/api/endpoint', { next: { revalidate: 3600 } }); // 可配置缓存时间 return res.json(); } export default async function RootLayout({ children }) { const navData = await getNavData(); return ( <html> <body> <Navbar data={navData} /> {children} <Footer /> </body> </html> ); }
不推荐你之前考虑的两个方案的原因
- 调整
index.js作为全局入口不生效:index.js仅对应根路由/,访问其他路由如/about时不会加载该文件,其他页面的导航栏无法拿到数据 - 每个页面单独调用API会造成冗余请求,切页时重复调用接口浪费资源,维护成本也更高
内容的提问来源于stack exchange,提问作者Peter Chavez
相关产品推荐
相关产品推荐

