Next.js+TypeScript中Zustand嵌套对象函数运行时报错'not a function'排查
问题原因
Zustand 的persist中间件默认会将状态序列化为 JSON 格式存储(如 localStorage),而函数无法被 JSON 序列化。当你把toggle_slide_over嵌套在session_setup对象内部时,这个函数会被 persist 过程中的序列化操作丢弃;从存储恢复状态时,session_setup.toggle_slide_over会变成undefined,调用自然会抛出"not a function"错误。
而将函数放在对象外部时,Zustand 会将其视为状态的动作(actions),不会被 persist 序列化,因此能正常保留。
修复方法
方案1:用partialize排除嵌套函数(保留原有结构)
通过persist的partialize配置,指定仅序列化需要持久化的原始数据(如slide_over_open),排除嵌套的函数:
const doPersist = persist( (set) => ({ session_setup: { slide_over_open: false, toggle_slide_over: (new_toggle_state) => set( produce(state => { state.session_setup.slide_over_open = new_toggle_state }) ), // 其他session_setup属性... }, // 其他顶层状态... }), { name: 'my-app-store', // 存储键名 partialize: (state) => ({ session_setup: { // 只序列化需要持久化的原始数据,跳过函数 slide_over_open: state.session_setup.slide_over_open // 其他需要持久化的session_setup属性... } // 其他需要持久化的顶层状态... }) } )
这样 persist 只会处理你指定的原始数据,嵌套函数不会被序列化,能正常保留。
方案2:拆分状态为独立 Slice(推荐)
如果状态结构复杂,推荐用 Zustand 的 Slice 模式,将session_setup拆分为独立的状态片段,函数放在顶层,既避免序列化问题,也让状态结构更清晰:
// 定义session_setup的独立Slice const createSessionSetupSlice = (set) => ({ session_slide_over_open: false, toggleSessionSlideOver: (new_toggle_state) => set( produce(state => { state.session_slide_over_open = new_toggle_state }) ), // 其他session_setup相关状态和方法... }) // 组合Slice创建Store const doPersist = persist( (set) => ({ ...createSessionSetupSlice(set), // 其他Slice... }), { name: 'my-app-store' } )
这种方式无需额外配置,且命名更简洁,多层嵌套场景下也不会出现冗长命名问题。
方案3:依赖初始状态合并逻辑(不推荐)
如果坚持保留嵌套结构,确保初始状态中session_setup始终包含toggle_slide_over函数。persist 恢复状态时,只会覆盖序列化的属性(如slide_over_open),初始状态里的函数会被保留。但这种方式依赖 persist 的合并逻辑,若存储的状态意外包含toggle_slide_over的序列化值(如null),仍会覆盖函数,可靠性较低。
验证修复
修改后,正常获取嵌套函数即可正常调用:
const toggle_slide_over = useMyAppStore(state => state.session_setup.toggle_slide_over)
内容的提问来源于stack exchange,提问作者kroflic

