- 修改 Zustand sidebar store 使用 skipHydration 避免 SSR 不匹配 - 移除 Redux auth slice 初始状态中的 localStorage 读取 - 在 providers 中使用 useEffect 初始化客户端状态 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
dc27044dab
commit
fc3efe6a27
|
|
@ -2,9 +2,21 @@
|
|||
|
||||
import { Provider as ReduxProvider } from 'react-redux';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { store } from '@/store';
|
||||
import { Toaster } from '@/components/ui/toaster';
|
||||
import { useSidebar } from '@/store/zustand/use-sidebar';
|
||||
import { initializeAuth } from '@/store/slices/auth.slice';
|
||||
|
||||
function HydrationHandler() {
|
||||
useEffect(() => {
|
||||
// 客户端初始化 zustand 持久化状态
|
||||
useSidebar.persist.rehydrate();
|
||||
// 客户端初始化 auth token
|
||||
store.dispatch(initializeAuth());
|
||||
}, []);
|
||||
return null;
|
||||
}
|
||||
|
||||
export function Providers({ children }: { children: React.ReactNode }) {
|
||||
const [queryClient] = useState(
|
||||
|
|
@ -22,6 +34,7 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
|||
return (
|
||||
<ReduxProvider store={store}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<HydrationHandler />
|
||||
{children}
|
||||
<Toaster />
|
||||
</QueryClientProvider>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ interface AuthState {
|
|||
|
||||
const initialState: AuthState = {
|
||||
user: null,
|
||||
token: typeof window !== 'undefined' ? localStorage.getItem('admin_token') : null,
|
||||
token: null,
|
||||
isAuthenticated: false,
|
||||
loading: false,
|
||||
error: null,
|
||||
|
|
@ -62,6 +62,14 @@ const authSlice = createSlice({
|
|||
setToken: (state, action: PayloadAction<string>) => {
|
||||
state.token = action.payload;
|
||||
},
|
||||
initializeAuth: (state) => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const token = localStorage.getItem('admin_token');
|
||||
if (token) {
|
||||
state.token = token;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
|
|
@ -97,5 +105,5 @@ const authSlice = createSlice({
|
|||
},
|
||||
});
|
||||
|
||||
export const { clearError, setToken } = authSlice.actions;
|
||||
export const { clearError, setToken, initializeAuth } = authSlice.actions;
|
||||
export default authSlice.reducer;
|
||||
|
|
|
|||
|
|
@ -1,21 +1,30 @@
|
|||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { persist, createJSONStorage } from 'zustand/middleware';
|
||||
|
||||
interface SidebarState {
|
||||
isCollapsed: boolean;
|
||||
_hasHydrated: boolean;
|
||||
toggle: () => void;
|
||||
setCollapsed: (collapsed: boolean) => void;
|
||||
setHasHydrated: (state: boolean) => void;
|
||||
}
|
||||
|
||||
export const useSidebar = create<SidebarState>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
isCollapsed: false,
|
||||
_hasHydrated: false,
|
||||
toggle: () => set((state) => ({ isCollapsed: !state.isCollapsed })),
|
||||
setCollapsed: (collapsed) => set({ isCollapsed: collapsed }),
|
||||
setHasHydrated: (state) => set({ _hasHydrated: state }),
|
||||
}),
|
||||
{
|
||||
name: 'sidebar-state',
|
||||
storage: createJSONStorage(() => localStorage),
|
||||
skipHydration: true,
|
||||
onRehydrateStorage: () => (state) => {
|
||||
state?.setHasHydrated(true);
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue