Inertia/React creates a race condition when changing pages, where resetting the scroll position to (0,0) often happens an instant before the page component itself changes to the next page
This creates a flash of unexpected content when you'll scrolled down a page and click a <Link>.
A huge caveat to all of this is that I'm brand new to Inertia as a user, let alone looking at this codebase, so please take anything I suggest with a grain of salt.
each framework package (react/vue/etc) can now specific in it's router.init whether it will handle resetting scroll position itself (vs letting the router core decide when to do it)
the swapComponent callback within router.init now passes a new argument, preserveScroll, similar to how it passes preserveState. This and (1) mean React has the information needed to manage scroll resetting in it's own render lifecycle
router.resetScrollPositions() is now public
Existing calls to router.resetScrollPositions() adjacent to the swapComponent call have been replaced with router.resetScrollPositionsIfNotHandledExternally() -- which just does nothing if the handleScrollResetsExternally flag is set by the framework package. Other calls to router.resetScrollPositions() weren't changed as they don't interact with the swapComponent function and don't seem to suffer the same issue
I've also updated the React playground with a sticky header so that the fix can be validated there as well.
Testing so far:
Tested the fix in the playground ✅
Linked the rebuild inertia into my own app, and the more aggressive flashes are gone ✅
Ensured that preserveScroll still works on Link and Router calls ✅
This is my (attempted) fix for https://github.com/inertiajs/inertia/issues/1802. I outlined the issue in detail there but tl;dr:
<Link>
.A huge caveat to all of this is that I'm brand new to Inertia as a user, let alone looking at this codebase, so please take anything I suggest with a grain of salt.
Based on how Remix/React-router handle this successfully with a
useLayoutEffect
, I've slightly adapted a few things:router.init
whether it will handle resetting scroll position itself (vs letting the router core decide when to do it)swapComponent
callback withinrouter.init
now passes a new argument,preserveScroll
, similar to how it passespreserveState
. This and (1) mean React has the information needed to manage scroll resetting in it's own render lifecyclerouter.resetScrollPositions()
is now publicrouter.resetScrollPositions()
adjacent to theswapComponent
call have been replaced withrouter.resetScrollPositionsIfNotHandledExternally()
-- which just does nothing if thehandleScrollResetsExternally
flag is set by the framework package. Other calls torouter.resetScrollPositions()
weren't changed as they don't interact with theswapComponent
function and don't seem to suffer the same issueI've also updated the React playground with a
sticky
header so that the fix can be validated there as well.Testing so far:
preserveScroll
still works onLink
andRouter
calls ✅