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

React中如何正确初始化结合encrypt-storage的zustand持久化状态?

问题

我正在使用zustand的persist插件存储应用状态,希望用localStorage但要对缓存加密,采用encrypt-storage工具实现加密。需要调用后端API获取加密密钥来初始化encrypt-storage,但API调用过程中storage仍为undefined,导致报错:Uncaught TypeError: can't access property "setItem", storage is undefined。

附上我尝试的代码:

import { EncryptStorage } from "encrypt-storage";
import { create } from "zustand";
import { devtools, persist, } from "zustand/middleware";
import { createJSONStorage } from "zustand/middleware"


const fake_api = (ms: number) => new Promise(resolve => {
  setTimeout(resolve, ms)
})

export function KeyResolver(callback: () => void) {
  const fn = async () => {
    // 模拟API请求获取密钥
    await fake_api(2000);
    console.log("encryption key retrieved")
    encryptStorage.EKEY = 'secret-key-value';
    encryptStorage.storage = new EncryptStorage(encryptStorage.EKEY, {
      stateManagementUse: true,
    });
    callback();
  };
  if (!encryptStorage.EKEY) {
    fn();
  }
}

interface IEncryptStorage {
  storage: undefined | EncryptStorage,
  EKEY: null | string,
}

export const encryptStorage: IEncryptStorage = {
  storage: undefined,
  EKEY: null,

}


const useOptimiserStore = create<IOptimiserStore>()(
  devtools(
    persist(
      (set) => ({
        ...initialOtimiserStoreState,
        _hasHydrated: false, 
        setHyderated: (val) => set({ _hasHydrated: val })
      }),
      {
        name: "optimiser-storage",

       
        // @ts-expect-error
        storage: createJSONStorage(() => encryptStorage.storage),

        onRehydrateStorage: () => {
          KeyResolver(() => {
            useOptimiserStore.getState().setHyderated(true)
          });

        }
      }
    ),
    {
      name: "optimiser-storage",
    }
  )
);

// 组件中使用方式:
const Component = () => {

  const hasHyderated = useOptimiserStore(state => state._hasHydrated);

  if (!hasHyderated) {
    return <>retreiving encryption keys </>
  }

  return <div> ... </div>

}
解决方案

核心问题是zustand初始化时encryptStorage.storage还未完成初始化,persist插件会立即尝试访问storage的方法,导致undefined报错。需要调整密钥获取和store初始化的顺序,确保storage准备好后再让persist生效。

方案一:异步初始化store(推荐)

把store的创建逻辑包装成异步函数,等密钥获取并初始化EncryptStorage后再执行:

import { EncryptStorage } from "encrypt-storage";
import { create } from "zustand";
import { devtools, persist, } from "zustand/middleware";
import { createJSONStorage } from "zustand/middleware"

const fake_api = (ms: number) => new Promise<string>(resolve => {
  setTimeout(() => resolve('secret-key-value'), ms)
})

// 异步获取密钥并初始化EncryptStorage
async function initEncryptStorage(): Promise<EncryptStorage> {
  const key = await fake_api(2000);
  console.log("encryption key retrieved");
  return new EncryptStorage(key, {
    stateManagementUse: true,
  });
}

// 定义store的核心逻辑
const createOptimiserStore = (storage: EncryptStorage) => create<IOptimiserStore>()(
  devtools(
    persist(
      (set) => ({
        ...initialOtimiserStoreState,
        _hasHydrated: false, 
        setHyderated: (val) => set({ _hasHydrated: val })
      }),
      {
        name: "optimiser-storage",
        storage: createJSONStorage(() => storage),
        onRehydrateStorage: () => (state) => {
          state?.setHyderated(true);
        }
      }
    ),
    {
      name: "optimiser-storage",
    }
  )
);

// 声明store变量
let useOptimiserStore: ReturnType<typeof createOptimiserStore>;

// 导出初始化方法
export async function initStore() {
  const storage = await initEncryptStorage();
  useOptimiserStore = createOptimiserStore(storage);
}

// 供组件使用的钩子
export function useOptimiserStoreHook() {
  if (!useOptimiserStore) {
    throw new Error("Store未初始化,请先调用initStore()");
  }
  return useOptimiserStore;
}

应用入口初始化

在应用入口文件(如main.tsx)中先执行初始化,再渲染组件:

import { initStore } from './path-to-your-store';

async function bootstrap() {
  await initStore();
  const root = ReactDOM.createRoot(document.getElementById('root')!);
  root.render(<App />);
}

bootstrap();

组件中使用

const Component = () => {
  const useStore = useOptimiserStoreHook();
  const hasHyderated = useStore(state => state._hasHydrated);

  if (!hasHyderated) {
    return <>正在获取加密密钥...</>
  }

  return <div> ... </div>
}

方案二:自定义延迟适配层(同步store)

如果希望保持store同步创建,可以自定义一个storage适配层,在storage未准备好时轮询等待:

// 自定义适配storage,等待encryptStorage初始化完成
const delayedStorage: Pick<Storage, "getItem" | "setItem" | "removeItem"> = {
  getItem: async (key) => {
    while (!encryptStorage.storage) {
      await new Promise(resolve => setTimeout(resolve, 100));
    }
    return encryptStorage.storage.getItem(key);
  },
  setItem: async (key, value) => {
    while (!encryptStorage.storage) {
      await new Promise(resolve => setTimeout(resolve, 100));
    }
    encryptStorage.storage.setItem(key, value);
  },
  removeItem: async (key) => {
    while (!encryptStorage.storage) {
      await new Promise(resolve => setTimeout(resolve, 100));
    }
    encryptStorage.storage.removeItem(key);
  }
};

// 修改store的persist配置
const useOptimiserStore = create<IOptimiserStore>()(
  devtools(
    persist(
      (set) => ({
        ...initialOtimiserStoreState,
        _hasHydrated: false, 
        setHyderated: (val) => set({ _hasHydrated: val })
      }),
      {
        name: "optimiser-storage",
        storage: createJSONStorage(() => delayedStorage),
        onRehydrateStorage: () => {
          KeyResolver(() => {
            useOptimiserStore.getState().setHyderated(true);
          });
        }
      }
    ),
    {
      name: "optimiser-storage",
    }
  )
);

这种方式不需要修改应用入口的渲染逻辑,通过轮询等待storage初始化完成,避免直接访问undefined的storage。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 21:01:44