在Gatsby中使用Zustand-persist结合localStorage遇构建错误求助
问题
在Gatsby应用中使用Zustand管理全局状态,其中一个状态切片通过zustand-persist同步到localStorage。依赖更新后,执行gatsby build时出现错误:
WebpackError: ReferenceError: localStorage is not defined
初始代码:
import create from 'zustand' import { configurePersist } from 'zustand-persist'; const { persist, purge } = configurePersist({ storage: localStorage })
尝试修改代码,判断浏览器环境后传入localStorage:
import { create } from 'zustand' import { configurePersist } from 'zustand-persist'; const isBrowser = typeof window !== "undefined"; const { persist, purge } = configurePersist({ storage: isBrowser ? window?.localStorage : null, // use `AsyncStorage` in react native // rootKey: 'root', // optional, default value is `root` })
但出现新错误:
TypeError: Cannot read properties of null (reading 'getItem')
想了解是否需要mock相关方法才能避免构建失败?有没有Gatsby专用的包可以处理该问题?
解决方案
1. Mock兼容的Storage对象
服务端渲染时不能传入null,需要提供一个模拟localStorage核心方法的对象,避免调用getItem等方法时触发报错:
import { create } from 'zustand' import { configurePersist } from 'zustand-persist'; const isBrowser = typeof window !== "undefined"; // 服务端用mock对象,实现空方法避免报错 const mockStorage = { getItem: () => null, setItem: () => {}, removeItem: () => {} }; const { persist, purge } = configurePersist({ storage: isBrowser ? window.localStorage : mockStorage, })
2. 改用Zustand官方Persist中间件
zustand-persist可能存在维护不及时的问题,建议直接使用Zustand自带的persist中间件(需安装zustand/middleware),它会自动处理服务端与客户端的环境差异:
import { create } from 'zustand' import { persist } from 'zustand/middleware' const useStore = create( persist( (set, get) => ({ // 你的状态定义,比如: count: 0, increment: () => set(state => ({ count: state.count + 1 })) }), { name: 'app-storage', // localStorage中存储的键名 storage: typeof window !== 'undefined' ? window.localStorage : undefined, } ) )
3. Gatsby专属延迟初始化方案
利用Gatsby的wrapRootElement钩子,在客户端挂载后再初始化带persist的状态,确保服务端渲染时不会执行localStorage相关代码:
- 在项目根目录创建
gatsby-browser.js和gatsby-ssr.js:
// gatsby-browser.js 和 gatsby-ssr.js import React from 'react' import { StoreProvider } from './src/store/StoreProvider' export const wrapRootElement = ({ element }) => { return <StoreProvider>{element}</StoreProvider> }
- 编写状态提供者组件,仅在客户端初始化带persist的store:
// src/store/StoreProvider.js import React, { useEffect, useState, createContext, useContext } from 'react' import { create } from 'zustand' import { persist } from 'zustand/middleware' // 创建上下文 const StoreContext = createContext(null) // 定义状态创建函数 const createAppStore = () => create( persist( (set) => ({ // 你的状态逻辑 count: 0, increment: () => set(s => ({ count: s.count + 1 })) }), { name: 'gatsby-app-store' } ) ) export const StoreProvider = ({ children }) => { const [store, setStore] = useState(null) useEffect(() => { // 仅在客户端初始化store setStore(createAppStore()) }, []) // 服务端渲染时返回加载占位或null if (!store) return null return <StoreContext.Provider value={store}>{children}</StoreContext.Provider> } // 自定义hook获取状态 export const useAppStore = () => useContext(StoreContext)
内容的提问来源于stack exchange,提问作者COMMAND CENTER
相关产品推荐
相关产品推荐

