mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-04 01:40:45 +08:00
fix(frontend): resolve URL path inconsistency caused by localStorage race condition
Fixed the issue where accessing the React frontend via ccw view with a URL path parameter would load a different workspace due to localStorage rehydration race condition. Root cause: zustand persist's onRehydrateStorage callback automatically called switchWorkspace with the cached projectPath before AppShell could process the URL parameter. Changes: - workflowStore.ts: Remove automatic switchWorkspace from onRehydrateStorage - AppShell.tsx: Centralize initialization logic with clear priority order: * Priority 1: URL ?path= parameter (explicit user intent) * Priority 2: localStorage fallback (implicit cache) * Added isWorkspaceInitialized state lock to prevent duplicate execution Fixes: URL showing ?path=/new/path but loading /old/path from cache
This commit is contained in:
@@ -41,22 +41,42 @@ export function AppShell({
|
||||
const projectPath = useWorkflowStore((state) => state.projectPath);
|
||||
const location = useLocation();
|
||||
|
||||
// Initialize workspace from URL path parameter on mount
|
||||
// Workspace initialization logic (URL > localStorage)
|
||||
const [isWorkspaceInitialized, setWorkspaceInitialized] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Only initialize if no workspace is currently set
|
||||
if (projectPath) return;
|
||||
|
||||
// Read path from URL query parameter
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const pathParam = searchParams.get('path');
|
||||
|
||||
if (pathParam) {
|
||||
console.log('[AppShell] Initializing workspace from URL:', pathParam);
|
||||
switchWorkspace(pathParam).catch((error) => {
|
||||
console.error('[AppShell] Failed to initialize workspace:', error);
|
||||
});
|
||||
// This effect should only run once to decide the initial workspace.
|
||||
if (isWorkspaceInitialized) {
|
||||
return;
|
||||
}
|
||||
}, [location.search, projectPath, switchWorkspace]);
|
||||
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const urlPath = searchParams.get('path');
|
||||
const persistedPath = projectPath; // Path from rehydrated store
|
||||
|
||||
let pathFound = false;
|
||||
|
||||
// Priority 1: URL parameter.
|
||||
if (urlPath) {
|
||||
console.log('[AppShell] Initializing workspace from URL parameter:', urlPath);
|
||||
switchWorkspace(urlPath).catch((error) => {
|
||||
console.error('[AppShell] Failed to initialize from URL:', error);
|
||||
});
|
||||
pathFound = true;
|
||||
}
|
||||
// Priority 2: Rehydrated path from localStorage.
|
||||
else if (persistedPath) {
|
||||
console.log('[AppShell] Initializing workspace from persisted state:', persistedPath);
|
||||
// The path is already in the store, but we need to trigger the data fetch.
|
||||
switchWorkspace(persistedPath).catch((error) => {
|
||||
console.error('[AppShell] Failed to re-initialize from persisted state:', error);
|
||||
});
|
||||
pathFound = true;
|
||||
}
|
||||
|
||||
// Mark as initialized regardless of whether a path was found.
|
||||
setWorkspaceInitialized(true);
|
||||
}, [isWorkspaceInitialized, projectPath, location.search, switchWorkspace]);
|
||||
|
||||
// Sidebar collapse state (persisted)
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
|
||||
|
||||
@@ -541,14 +541,10 @@ export const useWorkflowStore = create<WorkflowStore>()(
|
||||
if (state?.projectPath) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('[WorkflowStore] Found persisted projectPath, re-initializing workspace:', state.projectPath);
|
||||
console.log('[WorkflowStore] Rehydrated with persisted projectPath:', state.projectPath);
|
||||
}
|
||||
// Use setTimeout to ensure the store is fully initialized before calling switchWorkspace
|
||||
setTimeout(() => {
|
||||
if (state.switchWorkspace) {
|
||||
state.switchWorkspace(state.projectPath);
|
||||
}
|
||||
}, 0);
|
||||
// The initialization logic is now handled by AppShell.tsx
|
||||
// to correctly prioritize URL parameters over localStorage.
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user