From be69325797b03bd7c513790f3b0de235843a2be6 Mon Sep 17 00:00:00 2001 From: Yaojia Wang Date: Wed, 5 Nov 2025 20:56:13 +0100 Subject: [PATCH] fix(frontend): Fix Zustand authStore hydration timing issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix race condition where Epic form checked user authentication before Zustand persist middleware completed hydration from localStorage. Root cause: - authStore uses persist middleware to restore from localStorage - Hydration is asynchronous - Epic form checked user state before hydration completed - Result: "User not authenticated" error on page refresh Changes: - Add isHydrated state to authStore interface - Add onRehydrateStorage callback to track hydration completion - Update epic-form to check isHydrated before checking user - Disable submit button until hydration completes - Show "Loading..." button text during hydration - Improve error messages for better UX - Add console logging to track hydration process Testing: - Page refresh should now wait for hydration - Epic form correctly identifies logged-in users - Submit button disabled until auth state ready - Clear user feedback during loading state Fixes: Epic creation "User not authenticated" error on refresh 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- components/epics/epic-form.tsx | 16 ++++++++++++---- stores/authStore.ts | 12 ++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/components/epics/epic-form.tsx b/components/epics/epic-form.tsx index 20134a2..462a8a5 100644 --- a/components/epics/epic-form.tsx +++ b/components/epics/epic-form.tsx @@ -59,6 +59,7 @@ export function EpicForm({ projectId, epic, onSuccess, onCancel }: EpicFormProps const createEpic = useCreateEpic(); const updateEpic = useUpdateEpic(); const user = useAuthStore((state) => state.user); + const isHydrated = useAuthStore((state) => state.isHydrated); const form = useForm({ resolver: zodResolver(epicSchema), @@ -71,12 +72,19 @@ export function EpicForm({ projectId, epic, onSuccess, onCancel }: EpicFormProps }); async function onSubmit(data: EpicFormValues) { - console.log('[EpicForm] onSubmit triggered', { data, user: user?.id, projectId }); + console.log('[EpicForm] onSubmit triggered', { data, user: user?.id, projectId, isHydrated }); try { + // Check if auth store has completed hydration + if (!isHydrated) { + console.warn('[EpicForm] Auth store not hydrated yet, waiting...'); + toast.error('Loading user information, please try again in a moment'); + return; + } + if (!user?.id) { console.error('[EpicForm] User not authenticated'); - toast.error('User not authenticated'); + toast.error('Please log in to create an epic'); return; } @@ -239,9 +247,9 @@ export function EpicForm({ projectId, epic, onSuccess, onCancel }: EpicFormProps Cancel )} - diff --git a/stores/authStore.ts b/stores/authStore.ts index 9ffa237..9e5ca3d 100644 --- a/stores/authStore.ts +++ b/stores/authStore.ts @@ -6,6 +6,7 @@ interface AuthState { user: User | null; isAuthenticated: boolean; isLoading: boolean; + isHydrated: boolean; setUser: (user: User) => void; clearUser: () => void; @@ -18,6 +19,7 @@ export const useAuthStore = create()( user: null, isAuthenticated: false, isLoading: true, + isHydrated: false, setUser: (user) => set({ user, isAuthenticated: true, isLoading: false }), @@ -31,6 +33,16 @@ export const useAuthStore = create()( user: state.user, isAuthenticated: state.isAuthenticated, }), + onRehydrateStorage: () => (state) => { + console.log('[AuthStore] Hydration started'); + if (state) { + state.isHydrated = true; + console.log('[AuthStore] Hydration completed', { + user: state.user?.id, + isAuthenticated: state.isAuthenticated + }); + } + }, } ) );