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!!!!!!!!!!!

posted @ 2025-06-18 11:38  PEAR2020  阅读(81)  评论(0)    收藏  举报