NextJS 13.4.13服务器组件如何使用Zustand状态值?
解决方案:Next.js 13服务器组件中使用Zustand状态获取数据
方案一:拆分组件,客户端组件处理状态与数据请求
核心思路是保留Home作为服务器组件,将依赖Zustand状态的数据请求逻辑拆分到独立的客户端组件中,用SWR/React Query这类数据请求库处理异步逻辑(也可原生实现)。
步骤1:创建客户端组件CountryData.js
'use client'; import useStore from "@/utils/store"; import useSWR from 'swr'; // 复用数据请求函数 const fetcher = async (locale) => { const res = await fetch(`localhost:3001/api/${locale}`); if (!res.ok) throw new Error('Failed to fetch data'); return res.json(); }; export default function CountryData() { // 从Zustand获取状态 const countryCode = useStore((state) => state.countryCode); // 用SWR根据countryCode自动发起请求 const { data, error, isLoading } = useSWR(countryCode ? countryCode : null, fetcher); if (isLoading) return <div>加载中...</div>; if (error) return <div>错误:{error.message}</div>; return <MyComponent data={data} />; }
步骤2:修改page.js的Home组件
const Home = () => { return ( <main className='margin'> {/* 渲染客户端组件 */} <CountryData /> </main> ); }; export default Home;
Home依然是服务器组件,无需async,数据请求和状态依赖逻辑完全交给客户端组件处理。
方案二:通过URL参数传递状态,服务器组件直接获取
如果需要状态与URL同步(支持分享链接),可将选择的国家代码放到URL查询参数中,服务器组件从searchParams读取后发起请求。
步骤1:修改page.js的Home组件
import { Suspense } from 'react'; const getData = async (locale) => { const res = await fetch(`localhost:3001/api/${locale}`); if (!res.ok) throw new Error('Failed to fetch data'); return res.json(); }; // 抽离异步逻辑到子组件,用Suspense处理加载状态 async function DataComponent({ searchParams }) { // 从URL参数拿国家代码,默认用AU const countryCode = searchParams.country || 'AU'; const data = await getData(countryCode); return <MyComponent data={data} />; } const Home = () => { return ( <main className='margin'> <Suspense fallback={<div>加载中...</div>}> <DataComponent /> </Suspense> </main> ); }; export default Home;
步骤2:修改下拉选择组件(客户端组件)
'use client'; import useStore from "@/utils/store"; import { useRouter, useSearchParams } from 'next/navigation'; export default function CountrySelector() { const setCountryCode = useStore((state) => state.setCountryCode); const router = useRouter(); const searchParams = useSearchParams(); const handleChange = (e) => { const selectedCode = e.target.value; // 更新Zustand状态 setCountryCode(selectedCode); // 更新URL参数,触发服务器组件重新渲染 const newSearchParams = new URLSearchParams(searchParams); newSearchParams.set('country', selectedCode); router.push(`/?${newSearchParams.toString()}`); }; return ( <select onChange={handleChange}> <option value="AU">澳大利亚</option> <option value="US">美国</option> {/* 其他国家选项 */} </select> ); }
这种方案下,服务器组件可正常使用async发起请求,同时URL会同步当前选择的国家代码,状态一致性更好。
方案三:客户端组件用useEffect处理请求(无第三方库)
如果不想引入SWR/React Query,也可以用React原生的useEffect监听Zustand状态变化,触发数据请求:
'use client'; import useStore from "@/utils/store"; import { useState, useEffect } from 'react'; const getData = async (locale) => { const res = await fetch(`localhost:3001/api/${locale}`); if (!res.ok) throw new Error('Failed to fetch data'); return res.json(); }; export default function CountryData() { const countryCode = useStore((state) => state.countryCode); const [data, setData] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const fetchData = async () => { setLoading(true); try { const result = await getData(countryCode); setData(result); setError(null); } catch (err) { setError(err.message); } finally { setLoading(false); } }; // 当countryCode存在时发起请求 if (countryCode) fetchData(); }, [countryCode]); if (loading) return <div>加载中...</div>; if (error) return <div>错误:{error}</div>; if (!data) return <div>暂无数据</div>; return <MyComponent data={data} />; }
同样,Home组件只需渲染这个客户端组件即可。
内容的提问来源于stack exchange,提问作者kar
相关产品推荐
相关产品推荐

