Next.js + Zustand:Switched to client rendering because the server rendering errored 原因探索
🧩 前言
在使用 Zustand 进行状态管理时,我们很容易把注意力集中在组件本身,却忽略了状态模块背后的执行环境。当我在 Next.js App Router 中封装 useSortStore 自定义 hook 时,遇到一个看似莫名其妙的错误:
Error: Switched to client rendering because the server rendering errored: Cannot read properties of null (reading 'useSyncExternalStore')
这段报错是我意识到“不是组件也要写 use client”的起点。
😵💫 问题1
// store/useSortStore.ts import { useStore } from 'zustand' import { createStore } from 'zustand/vanilla' const sortStore = createStore(() => ({ sortTag: 'latest', setSort: (value) => set({ sortTag: value }), })) const useSortStore = (selector) => useStore(sortStore, selector) export default useSortStore // components/Sort.tsx "use client" import useSortStore from '@/store/useSortStore' export function Sort() { const sortTag = useSortStore(s => s.sortTag) // ... }
你以为没问题?但其实还是会在 SSR 阶段崩溃。
🧠 原因解析:模块顶层的“副作用”也会在 SSR 中运行
虽然组件是 "use client" 的,但 useSortStore.ts 模块没有 use client,所以:
-
它会被当作 Server Module 加载
-
导致模块顶层执行
createStore()并导入useStore()等 React Hook -
这些行为在 SSR 中触发
useSyncExternalStore(null)报错
即使你从未在服务端主动调用这些代码,只要组件中 import 了它,就会连带执行 SSR 预加载阶段的副作用。
✅ 结论:只要模块中用了 hook,不管是不是组件,都必须标记 "use client"
😵💫问题升级:标记"use client", 依然尝试在server运行?
彻底解决:zustand中createStore需要懒加载。在客户端组件初始化的时候,再运行!
let _store: StoreApi<SortStore> | null = null function initSortStore() { return createStore<SortStore>((set) => ({ sortTag: 'latest', setSort: (tag) => set({ sortTag: tag }), })) } function getStore(): StoreApi<SortStore> { if (!_store) { _store = initSortStore() } return _store } function useSortStore<T>(selector: (state: SortStore) => T) { const store = getStore() return useStore(store, selector) } export default useSortStore
✅ 结论:zustand的全局store创建,需要lazy initialization
😵💫 可能仍然有问题?
✅ 稳定写法建议
-
✅ 必须放在文件最顶部(import 之前);
-
✅ 必须使用 双引号:
"use client";
😡 还是有问题,真相是?
推翻了以上所有结论,答案是使用create-next-app 的默认npm run dev 会使用Turbopack(热更新还有bug!!!),而非 webpack!!!
所以问题出在Turbopack!!!!!!!!!
解决:
1. 安装 npm install --save-dev cross-env
2. 修改package.json
"scripts": { "dev": "cross-env TURBOPACK=0 next dev" }
不要用Turbopack!!!!!!!!!!!

浙公网安备 33010602011771号