You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何强制Zustand仅在客户端运行?解决自定义持久化中间件hydration错误

解决Zustand持久化中间件的Hydration错误问题

Hydration错误的核心原因是服务端渲染时没有LocalStorage,导致服务端初始化的store数据和客户端不一致。下面是几种可行的解决思路,让你的持久化逻辑仅在客户端生效:

方法1:用useEffect延迟客户端初始化

将store的初始值设为服务端兼容的默认值,然后在客户端通过useEffect触发LocalStorage数据加载,确保这段逻辑只在浏览器环境执行:

首先完善store,添加修改items的方法:

// Zustand部分修改
const localStorageMiddleware: Middleware<Store> = (config) => (set, get, api) =>
  config(
    (args) => {
      set(args);
      if (isRunningInBrowser) LocalStorage.Modify(get().items);
    },
    get,
    api,
  );

export const useStore = create<Store>(
  localStorageMiddleware((set) => ({
    items: [],
    // 新增修改items的方法
    setItems: (newItems) => set({ items: newItems })
  }))
);

// 新增客户端初始化hook
export const useInitializeStore = () => {
  const setItems = useStore(state => state.setItems);
  
  useEffect(() => {
    // 仅在客户端读取LocalStorage并更新store
    const savedItems = LocalStorage.Read();
    setItems(savedItems);
  }, [setItems]);
};

然后在你的根组件(如Next.js的_app.tsx)中调用这个初始化hook:

import { useInitializeStore } from '@/store';

function MyApp({ Component, pageProps }) {
  useInitializeStore();
  
  return <Component {...pageProps} />;
}

export default MyApp;

方法2:改进中间件,在初始化阶段区分环境

让中间件在初始化时直接根据环境返回对应状态,确保服务端返回默认值,客户端返回LocalStorage的数据:

// 先确保环境判断准确
export const isRunningInBrowser = typeof window !== 'undefined';

const localStorageMiddleware: Middleware<Store> = (config) => (set, get, api) => {
  const wrappedSet = (args) => {
    set(args);
    if (isRunningInBrowser) {
      LocalStorage.Modify(get().items);
    }
  };

  // 先获取基础初始状态
  const baseState = config(wrappedSet, get, api);
  
  // 客户端环境下覆盖items为LocalStorage数据
  if (isRunningInBrowser) {
    return {
      ...baseState,
      items: LocalStorage.Read()
    };
  }
  
  return baseState;
};

export const useStore = create<Store>(
  localStorageMiddleware((set) => ({
    items: [],
    setItems: (newItems) => set({ items: newItems })
  }))
);

方法3:动态导入store,禁用SSR

如果你的项目用了Next.js这类SSR框架,可以通过动态导入让整个store仅在客户端加载:

// 在使用store的组件中
import dynamic from 'next/dynamic';

// 动态导入store,禁用服务端渲染
const useStore = dynamic(() => import('@/store').then(mod => mod.useStore), {
  ssr: false
});

注意事项

  • 确保isRunningInBrowser的判断准确,必须用typeof window !== 'undefined',不能直接检查window是否存在(服务端会报错)。
  • 原代码中useStore的括号存在语法错误,已在示例中修正()}改为})))。

内容的提问来源于stack exchange,提问作者Ali Bahaari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 22:05:02