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.
This commit is contained in:
Tom Butcher 2026-09-21 00:20:32 +01:00
parent d348d6f2b3
commit 4f6f7cb881
2 changed files with 44 additions and 14 deletions

View File

@ -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 = () => (
<TableStateProvider>
<Outlet />
@ -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) {
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,
reuseTabLocation(
reuseTabLocation(prevActiveId, previousFrozen, lastLoc)
)
} else if (!previousFrozen && previousTab) {
frozenRef.current.set(
prevActiveId,
frozenRef.current.get(prevActiveId),
lastLocationRef.current
)
entryToLocation(getTabCurrentEntry(previousTab), lastLoc, prevActiveId)
)
}
pendingRestoreRef.current = true
}

View File

@ -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
}
// 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.)
if (restoring.previousTabId) {
applyEntryToTabId(restoring.previousTabId, entry)
if (foreign) foreign.entry = entry
}
return
} else {
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