Next13服务端组件如何基于动态chainId获取并刷新数据?
可行方案推荐
针对你在Next.js 13 App Router中遇到的服务端组件无法获取客户端Context中chainId的问题,这里有三种实用方案,都能保持LottosPage为服务端组件,同时实现切换chainId时自动重新获取数据:
方案1:将chainId作为URL搜索参数(最简便)
把chainId存在URL的查询参数里,服务端组件可以直接通过searchParams props获取,切换时自动触发服务端组件重新渲染。
步骤:
- 客户端下拉框组件(需标记
'use client')
'use client'; import { useRouter, useSearchParams } from 'next/navigation'; export default function ChainSelector() { const router = useRouter(); const searchParams = useSearchParams(); const currentChainId = searchParams.get('chainId') || '1'; // 设置默认chainId const handleChainChange = (e: React.ChangeEvent<HTMLSelectElement>) => { const newChainId = e.target.value; // 更新URL的查询参数,自动触发服务端组件重新获取数据 router.push({ searchParams: { chainId: newChainId }, }); }; return ( <select value={currentChainId} onChange={handleChainChange}> <option value="1">Ethereum</option> <option value="5">Goerli</option> <option value="137">Polygon</option> </select> ); }
- 服务端组件
LottosPage
// 无需标记'use client',天然是服务端组件 export default async function LottosPage({ searchParams, }: { searchParams: { chainId?: string }; }) { const chainId = searchParams.chainId || '1'; // 基于chainId请求数据 const lottoData = await fetch(`https://your-api.com/lottos?chainId=${chainId}`).then(res => res.json()); return ( <div> <h1>Chain {chainId} 彩票列表</h1> <ul> {lottoData.map((lotto: any) => ( <li key={lotto.id}>{lotto.name}</li> ))} </ul> </div> ); }
- 在
layout.tsx中引入下拉框
直接把ChainSelector放在导航栏或页面顶部即可,无需额外处理。
方案2:将chainId存在Cookie中
把chainId存储到Cookie里,服务端组件通过next/headers的cookies()函数读取,切换时刷新路由触发重新获取。
步骤:
- 客户端下拉框组件
'use client'; import { useRouter, cookies } from 'next/navigation'; export default function ChainSelector() { const router = useRouter(); const cookieStore = cookies(); const currentChainId = cookieStore.get('chainId')?.value || '1'; const handleChainChange = (e: React.ChangeEvent<HTMLSelectElement>) => { const newChainId = e.target.value; // 设置Cookie cookieStore.set('chainId', newChainId, { path: '/' }); // 刷新路由让服务端组件读取新Cookie router.refresh(); }; return ( <select value={currentChainId} onChange={handleChainChange}> <option value="1">Ethereum</option> <option value="5">Goerli</option> <option value="137">Polygon</option> </select> ); }
- 服务端组件
LottosPage
import { cookies } from 'next/headers'; export default async function LottosPage() { const chainId = cookies().get('chainId')?.value || '1'; const lottoData = await fetch(`https://your-api.com/lottos?chainId=${chainId}`).then(res => res.json()); return ( <div> <h1>Chain {chainId} 彩票列表</h1> <ul> {lottoData.map((lotto: any) => ( <li key={lotto.id}>{lotto.name}</li> ))} </ul> </div> ); }
方案3:使用动态路由段(语义化更强)
把chainId作为路由的一部分(如/lottos/[chainId]),服务端组件通过params props获取,适合需要URL语义化的场景。
步骤:
- 创建动态路由页面
在app/lottos/[chainId]/page.tsx中编写服务端组件:
export default async function LottosPage({ params }: { params: { chainId: string } }) { const chainId = params.chainId; const lottoData = await fetch(`https://your-api.com/lottos?chainId=${chainId}`).then(res => res.json()); return ( <div> <h1>Chain {chainId} 彩票列表</h1> <ul> {lottoData.map((lotto: any) => ( <li key={lotto.id}>{lotto.name}</li> ))} </ul> </div> ); }
- 客户端下拉框组件
'use client'; import { useRouter, usePathname } from 'next/navigation'; export default function ChainSelector() { const router = useRouter(); const pathname = usePathname(); const currentChainId = pathname.split('/')[2] || '1'; const handleChainChange = (e: React.ChangeEvent<HTMLSelectElement>) => { const newChainId = e.target.value; router.push(`/lottos/${newChainId}`); }; return ( <select value={currentChainId} onChange={handleChainChange}> <option value="1">Ethereum</option> <option value="5">Goerli</option> <option value="137">Polygon</option> </select> ); }
方案选择建议
- 如果你只是临时切换chainId获取数据,**方案1(搜索参数)**最简便,无需修改路由结构,刷新页面状态也能保留。
- 如果你需要持久化用户的chainId选择(下次打开页面自动加载),**方案2(Cookie)**更合适。
- 如果你希望URL语义化更强,把chainId作为资源标识,**方案3(动态路由)**是更好的选择。
内容的提问来源于stack exchange,提问作者Adham
相关产品推荐
相关产品推荐

