mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +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 projectPath = useWorkflowStore((state) => state.projectPath);
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
// Initialize workspace from URL path parameter on mount
|
// Workspace initialization logic (URL > localStorage)
|
||||||
|
const [isWorkspaceInitialized, setWorkspaceInitialized] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Only initialize if no workspace is currently set
|
// This effect should only run once to decide the initial workspace.
|
||||||
if (projectPath) return;
|
if (isWorkspaceInitialized) {
|
||||||
|
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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, [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)
|
// Sidebar collapse state (persisted)
|
||||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
|
||||||
|
|||||||
@@ -541,14 +541,10 @@ export const useWorkflowStore = create<WorkflowStore>()(
|
|||||||
if (state?.projectPath) {
|
if (state?.projectPath) {
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
// eslint-disable-next-line no-console
|
// 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
|
// The initialization logic is now handled by AppShell.tsx
|
||||||
setTimeout(() => {
|
// to correctly prioritize URL parameters over localStorage.
|
||||||
if (state.switchWorkspace) {
|
|
||||||
state.switchWorkspace(state.projectPath);
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user