Skip to content

fix(react-router): dispatch view lifecycle events on non-animated transitions - #31497

Merged
ShaneK merged 2 commits into
mainfrom
fix/rr6-lifecycle-non-animated
Sep 30, 2026
Merged

ShaneK merged 2 commits into
mainfrom
fix/rr6-lifecycle-non-animated

Conversation

@ShaneK

@ShaneK ShaneK commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Issue number: resolves #31479


What is the current behavior?

Currently, switching tabs or following a link with routerDirection="none" fires none of the four view lifecycle events, so a page that loads its data in useIonViewWillEnter renders empty. The non-animated branch of StackManager.transitionPage() skips routerOutlet.commit() and swaps the page classes itself to avoid intermediate paints, and core dispatches all four events from inside commit(), so they never fire. A capturing listener on the ion-router-outlet gets nothing either, so it isn't a problem with the hooks.

What is the new behavior?

That branch now dispatches the four events itself, in the same order core's transition() uses. The class swap moved to after all four, so the leaving page is still on screen for its leave events and the entering page is revealed only once they've fired. That's what core does on the animated path, where beforeTransition un-hides the leaving page and ion-page-hidden only goes back on after commit() resolves, and it matters because ion-page-hidden is display: none, so a useIonViewDidLeave handler reading scrollTop off the outgoing page was getting 0.

The two dispatch sites that already existed in this file for out-of-scope and root navigation now share the same helper.

Does this introduce a breaking change?

  • Yes
  • No

Other information

The events used to come from core here. This path called commit(enteringEl, undefined, { duration: 0 }) until 12f0d5e dropped it to fix a white flash during tab switches, and with no leaving element core took its noAnimation branch and fired the enter pair, so the enter half of this is a regression from that commit. Putting the call back would get the events plus focus and z-index handling from core for free, but it risks the flash it was removed for, so this dispatches them directly instead.

The patch on the issue fires the enter events unconditionally. This gates them on the generation check so a superseded transition doesn't announce an entry, and it skips the leave events when the leaving page already has ion-page-hidden, because two transitions sharing a leaving element would otherwise run its useIonViewDidLeave teardown twice. The check is the class rather than isViewVisible since a nested outlet marks its leaving page visibility: hidden before we get here and still needs ionViewDidLeave to unmount.

Preview:

Current Dev Build

9.0.6-dev.11790615145.1d8c56a2

@ShaneK
ShaneK requested a review from a team as a code owner September 28, 2026 17:04
@vercel

vercel Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
ionic-framework Ready Ready Preview Sep 30, 2026 3:44pm UTC

Request Review

@thetaPC thetaPC left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

}
};

type ViewLifecycleEvent = 'ionViewWillEnter' | 'ionViewDidEnter' | 'ionViewWillLeave' | 'ionViewDidLeave';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could these types come from core? My concern is maintaining multiple spots.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I was resistant to add an explicit core dependency in this change, but I guess it's already implicit (and technically only works because npm and yarn hoist core). Done here: 5b7a386

@ShaneK
ShaneK added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit f01e9a7 Sep 30, 2026
101 of 103 checks passed
@ShaneK
ShaneK deleted the fix/rr6-lifecycle-non-animated branch September 30, 2026 16:52

This branch was successfully deployed

1 active deployment
Preview — 5b7a386c Deployed Sep 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

package: react @ionic/react package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: (react-router): lifecycle events do not fire on tab switches or routerDirection="none" navigation

2 participants