ShaneK · GitHub

Issue number: resolves #29413


What is the current behavior?

In viewStacks.ts, unmountLeavingViews and mountIntermediaryViews walk the outlet's view stack using startIndex - delta (or startIndex + delta) as the loop end, with no bound on viewStack.length. delta comes from the popstate event's history delta. Apps that mount <ion-tabs> at the root with no outer <ion-router-outlet> only have one outlet registered, so usingLinearNavigation is true and these helpers actually run. Each tab switch adds a browser history entry but reuses existing view items, so |delta| can easily exceed the stack depth above the entering view. The loop then reads viewStack[i] as undefined and throws TypeError: viewItem is undefined from viewItem.mount = false. The navigation aborts mid-transition, which is what surfaces the secondary enteringEl is undefined warning and leaves that route stuck

What is the new behavior?

Both helpers bail when the entering view item isn't in the stack (startIndex === -1) and clamp the loop end to Math.min(viewStack.length, ...), so a delta that overruns the stack stops at the last real view item instead of indexing past the end. A new Vitest spec at packages/vue/test/base/tests/unit/tabs-single-outlet.spec.ts mounts <ion-tabs> as the app root with flat routes, builds up history across tabs and sub-pages, then calls router.go(-6). Without the fix the spec catches the unhandled TypeError from viewStacks.ts

Does this introduce a breaking change?

  • Yes
  • No

Other information

Main-branch counterpart of #31145. Source change is byte-identical to the source portion of that PR so the merge back into major-9.0 collapses to a no-op. There are no tests here, but there are in the v9 version because the v9 testing setup is better.

Read the original on github.com ↗