inertiajs / inertia

Inertia.js lets you quickly build modern single-page React, Vue and Svelte apps using classic server-side routing and controllers.
https://inertiajs.com
MIT License
6.03k stars 405 forks source link

React: Fix a flash of scrolling-to-top on page changes by using `useLayoutEffect` #1803

Open oscarnewman opened 4 months ago

oscarnewman commented 4 months ago

This is my (attempted) fix for https://github.com/inertiajs/inertia/issues/1802. I outlined the issue in detail there but tl;dr:

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:

  1. 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)
  2. 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
  3. router.resetScrollPositions() is now public
  4. 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:

  1. Tested the fix in the playground ✅
  2. Linked the rebuild inertia into my own app, and the more aggressive flashes are gone ✅
  3. Ensured that preserveScroll still works on Link and Router calls ✅