From 4f6f7cb881e44349e039850b42fac190f2e58d9b Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Mon, 21 Sep 2026 00:20:32 +0100 Subject: [PATCH] Improve State Management and Location Handling in Dashboard Tabs - Updated state handling in DashboardTabPanes to ensure null safety for tab state properties. - Introduced a new utility function to determine if a location belongs to a leaving tab, enhancing tab navigation logic. - Refactored location comparison logic to improve accuracy when checking for equality between tab states. - Enhanced NavigationTabsContext to manage same-path query cleanup, improving tab restoration behavior. --- .../Dashboard/common/DashboardTabPanes.jsx | 32 +++++++++++++------ .../context/NavigationTabsContext.jsx | 26 ++++++++++++--- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/src/components/Dashboard/common/DashboardTabPanes.jsx b/src/components/Dashboard/common/DashboardTabPanes.jsx index a381cd10..cadd7f77 100644 --- a/src/components/Dashboard/common/DashboardTabPanes.jsx +++ b/src/components/Dashboard/common/DashboardTabPanes.jsx @@ -24,7 +24,7 @@ const toTabLocation = (tabId, source) => ({ pathname: source?.pathname || '/', search: source?.search ?? '', hash: source?.hash ?? '', - state: source?.state, + state: source?.state ?? null, key: `tab-${tabId}` }) @@ -33,7 +33,7 @@ const reuseTabLocation = (tabId, previous, next) => { previous && locationsEqual(previous, next) && previous.key === `tab-${tabId}` && - previous.state === next.state + (previous.state ?? null) === (next.state ?? null) ) { return previous } @@ -45,7 +45,7 @@ const entryToLocation = (entry, fallbackLocation, tabId) => pathname: entry?.pathname || fallbackLocation.pathname, search: entry?.search ?? fallbackLocation.search ?? '', hash: entry?.hash ?? fallbackLocation.hash ?? '', - state: fallbackLocation.state + state: entry?.state ?? null }) const getTabCurrentEntry = (tab) => @@ -57,6 +57,12 @@ const tabMatchesLocation = (tab, loc) => { return locationsEqual(entry, loc) } +const locationBelongsToLeavingTab = (tab, frozen, loc) => { + if (!loc) return false + if (frozen && locationsEqual(frozen, loc)) return true + return tabMatchesLocation(tab, loc) +} + const CachedTabRouteLayout = () => ( @@ -67,7 +73,7 @@ const areLocationsEqual = (left, right) => left === right || (locationsEqual(left, right) && left?.key === right?.key && - left?.state === right?.state) + (left?.state ?? null) === (right?.state ?? null)) const arePanePropsEqual = (prev, next) => prev.tabId === next.tabId && @@ -166,14 +172,20 @@ const DashboardTabPanes = () => { const switchedTabs = Boolean(prevActiveId && activeTabId && prevActiveId !== activeTabId) if (switchedTabs) { - frozenRef.current.set( - prevActiveId, - reuseTabLocation( + const previousTab = tabs.find((tab) => tab.id === prevActiveId) + const previousFrozen = frozenRef.current.get(prevActiveId) + const lastLoc = lastLocationRef.current + if (locationBelongsToLeavingTab(previousTab, previousFrozen, lastLoc)) { + frozenRef.current.set( prevActiveId, - frozenRef.current.get(prevActiveId), - lastLocationRef.current + reuseTabLocation(prevActiveId, previousFrozen, lastLoc) ) - ) + } else if (!previousFrozen && previousTab) { + frozenRef.current.set( + prevActiveId, + entryToLocation(getTabCurrentEntry(previousTab), lastLoc, prevActiveId) + ) + } pendingRestoreRef.current = true } diff --git a/src/components/Dashboard/context/NavigationTabsContext.jsx b/src/components/Dashboard/context/NavigationTabsContext.jsx index 7503e655..2944dc14 100644 --- a/src/components/Dashboard/context/NavigationTabsContext.jsx +++ b/src/components/Dashboard/context/NavigationTabsContext.jsx @@ -257,6 +257,13 @@ const applyLocationToTab = (tab, entry) => { if (!tab.history?.length) { return { ...tab, history: [entry], historyIndex: 0 } } + const currentPath = currentEntry?.pathname || '/' + const nextPath = entry.pathname || '/' + if (currentPath === nextPath) { + const history = [...tab.history] + history[tab.historyIndex] = entry + return { ...tab, history } + } const truncated = tab.history.slice(0, tab.historyIndex + 1) return { ...tab, @@ -858,12 +865,17 @@ export const NavigationTabsProvider = ({ children }) => { if (entriesEqual(restoring.from, entry)) { return } - // A delayed write from the previous tab (viewId, filters, etc.) - if (restoring.previousTabId) { + // Same-path query cleanup on the tab we just opened (action params, etc.) + if (entry.pathname === restoring.target?.pathname) { + isRestoringRef.current = null + } else if (restoring.previousTabId) { + // A delayed write from the previous tab (viewId, filters, etc.) applyEntryToTabId(restoring.previousTabId, entry) if (foreign) foreign.entry = entry + return + } else { + return } - return } if (shouldIgnoreRestoredLocation(entry)) return @@ -877,7 +889,13 @@ export const NavigationTabsProvider = ({ children }) => { if (entriesEqual(entry, foreign.entry)) { return } - if (entry.pathname === foreign.entry.pathname) { + const activeEntry = getTabCurrentEntry( + tabsRef.current.find((tab) => tab.id === activeTabIdRef.current) + ) + if ( + entry.pathname === foreign.entry.pathname && + entry.pathname !== activeEntry?.pathname + ) { applyEntryToTabId(foreign.tabId, entry) foreign.entry = entry return