在Next.js App Router中如何将服务端组件数据存入Jotai
问题解决思路与代码修改
报错原因解析
你的代码触发报错的核心原因有两个:
- Jotai的
useAtom依赖React的useEffect客户端钩子,而Next.js App Router中组件默认是服务端组件,不允许使用客户端钩子。 - 服务端直接调用
categoriesAtom.set(data)无效:Jotai的默认状态存储在服务端和客户端是隔离的,服务端没有React上下文支持直接修改客户端状态。
解决方案:服务端取数 + 客户端Jotai初始化
我们需要拆分逻辑:服务端组件负责获取数据,客户端组件负责管理Jotai状态并渲染,同时通过全局Provider实现全站状态共享。
步骤1:定义全局Atom
创建单独的原子文件(比如src/atoms/categories.ts),统一管理分类状态:
import { atom } from 'jotai'; // 定义分类原子,初始值为空数组 export const categoriesAtom = atom([]);
步骤2:根Layout服务端取数并初始化Store
在根layout.tsx(服务端组件)中获取数据,并用Jotai的Store实现服务端到客户端的状态同步:
import { createStore } from 'jotai/vanilla'; import { Provider } from 'jotai'; import { categoriesAtom } from '@/atoms/categories'; import HeaderLogged from '@/components/Header/HeaderLogged'; // 服务端获取分类数据 async function fetchCategories() { const res = await fetch('https://api.escuelajs.co/api/v1/categories', { cache: 'force-cache' // 根据需求调整缓存策略 }); if (!res.ok) throw new Error('获取分类数据失败'); return res.json(); } export default async function RootLayout({ children }: { children: React.ReactNode }) { const categories = await fetchCategories(); // 创建全局Store并设置初始值 const store = createStore(); store.set(categoriesAtom, categories); return ( <html lang="zh-CN"> <body> {/* 全局注入Jotai Store,确保全站客户端组件都能访问状态 */} <Provider store={store}> <HeaderLogged /> {children} </Provider> </body> </html> ); }
步骤3:客户端组件使用Jotai状态
修改HeaderLogged.tsx,添加use client标记转为客户端组件,直接使用全局状态:
'use client'; // 必须放在文件最顶部,标记为客户端组件 import { useAtom } from 'jotai'; import { categoriesAtom } from '@/atoms/categories'; import MainNav2Logged from './MainNav2Logged'; export interface HeaderLoggedProps {} const HeaderLogged = () => { const [categories] = useAtom(categoriesAtom); return ( <div className="nc-HeaderLogged sticky top-0 w-full z-40 "> <MainNav2Logged /> {categories.map((el) => ( <p key={el.id}>is it here? {el.name}</p> ))} </div> ); }; export default HeaderLogged;
关键注意事项
use client必须放在客户端组件的第一行,明确告知Next.js这是客户端组件。- 服务端
fetch可以通过cache参数控制数据缓存策略,比如revalidate: 3600实现每小时刷新一次数据。 - 通过全局
Provider注入Store后,所有客户端组件都能通过useAtom访问分类状态,实现全站共享。
内容的提问来源于stack exchange,提问作者EshgheCode
相关产品推荐
相关产品推荐

