¿Cómo configuro React Router 6 para restaurar la posición de desplazamiento cuando navego y cuando se actualiza la ventana del navegador?
React Router 5 tiene una página sobre restauración de desplazamiento , pero no puedo encontrar nada sobre desplazamiento en los documentos para v6 , así que supongo que se supone que debes manejar esto tú mismo con otro paquete. Está bien, pero no puedo encontrar nada que sea compatible con React Router 6.
Los paquetes react-scroll-restoration y oaf-react-router requieren v5. (oaf-react-router enumera que es compatible con v6, pero el ejemplo de código de uso básico no es compatible con v6, y el problema relacionado #210 aún está abierto).
Gatsby y Next.js admiten la restauración de desplazamiento desde el primer momento, pero no parece que haya un paquete cuidadosamente empaquetado que pueda usar.
Esta pequeña aplicación de demostración con páginas renderizadas del lado del servidor hace lo que quiero. La posición de desplazamiento se restaura cuando se navega de un lado a otro y se actualiza la ventana del navegador.
Aquí está la misma aplicación que usa React Router 6 , donde la posición de desplazamiento no se guarda ni se restaura, sino que se reutiliza entre páginas. La solución habitual para eso es desplazarse hacia la parte superior cada vez que se navega por la página, pero no estoy interesado en ese comportamiento.
Editar: React Query escribe que el problema con la restauración de desplazamiento es que las páginas están recuperando datos , lo que implica que si los datos para representar las páginas están allí, la restauración de desplazamiento simplemente funciona. No puedo confirmar eso, porque mi pequeña aplicación React Router 6 tiene el problema incluso sin obtener ningún dato. Siento que hay algo pequeño que creo que me falta para que funcione.
Rant: Estoy bastante sorprendido de que la respuesta típica a este problema sea llamar a window.scrollTo(0, 0) al navegar. Esto solo soluciona el problema de la transferencia de la posición de desplazamiento entre páginas. Cuando no se restablece la posición de desplazamiento, la experiencia del usuario al navegar entre páginas se deteriora gravemente. Supongo que esta es en parte la razón por la que las ventanas emergentes se han vuelto tan populares, pero traen una larga serie de otros problemas de UX, por lo que realmente quiero evitar usarlas.
Gracias a este comentario en oaf-react-router pude hacerlo funcionar con React Router 6. Sin embargo, hay algunas advertencias, por lo que no considero que esta sea una solución viable para una aplicación web profesional.
Como se indica en este comentario de código , oaf-react-router tiene que usar la misma versión del history que react-router-dom . Es por eso que HistoryRouter se exporta como unstable_HistoryRouter . De hecho, esta solución se siente bastante inestable.
oaf-react-router no restaura la posición de desplazamiento al actualizar una página web. No sé si esto se puede lograr fácilmente, y es algo que podría ser aceptable.
import { createBrowserHistory } from 'history'; import { wrapHistory } from 'oaf-react-router'; import React from 'react'; import ReactDOM from 'react-dom'; import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom'; const history = createBrowserHistory(); wrapHistory(history); ReactDOM.render( <React.StrictMode> <HistoryRouter history={history}> <App /> </HistoryRouter> </React.StrictMode>, document.getElementById('root') );Usando createBrowserHistory y almacenando yscroll en el almacenamiento de la sesión, uno puede lograr esto. Consulte el siguiente enlace: https://medium.com/@knl.shivam.gupta/react-hooks-scroll-position-using-react-router-dom-v6-6cd59730b18d
Aquí, el location.state en v6 se usa para almacenar el yscroll en la pila del historial del navegador, lo que hace posible manipular la pantalla usando useLayoutEffect.
Sé que ya publiqué una respuesta, pero terminé con una solución diferente y creo que merece una respuesta por separado.
Lo primero que tuve que darme cuenta es que StackBlitz y CodeSandbox tienen un manejo de ruta personalizado que interrumpe la restauración del desplazamiento . Este problema sigue ahí cuando se abre la aplicación de demostración en una ventana separada. Si la aplicación se ejecuta en un entorno normal, la posición de desplazamiento se restaura al navegar entre páginas usando los botones de avance y retroceso del navegador.
Quedan estos dos problemas:
La posición de desplazamiento se mantiene entre las páginas al navegar usando enlaces en la aplicación. Técnicamente cuando se usa <Link to="..."/> de React Router o navigate(...) . La página debe desplazarse hasta la parte superior.
Resumen: Vaya a la página A, desplácese hacia abajo y haga clic en un enlace que lo lleve a la página B. Si navega de regreso a A usando el botón Atrás del navegador, la posición de desplazamiento debe restaurarse. Si navega de regreso a A usando un enlace a A en la página B, la posición de desplazamiento debe estar en la parte superior de la página.
La posición de desplazamiento de una página debe restaurarse (mantenerse) al actualizar la página. Las páginas se desplazan hacia arriba al actualizar.
Resolví el primer problema con el siguiente código y acepté que el segundo problema sigue siendo un problema. Creo que Gatsby y Next.js pueden resolver el problema de actualización almacenando la posición de desplazamiento en el almacenamiento de la sesión, y eso me pareció una exageración para mi caso de uso.
import { MouseEventHandler, ReactNode } from "react"; import { Link } from "react-router-dom"; import { useNavigateToTop } from "./useNavigateToTop"; interface Props { children: ReactNode; className?: string; to: string; } /** Link to the top of a page so that the scroll position isn't persisted between pages. Use this instead of React's build-in @see {@link Link}. */ export const LinkToTop = (props: Props) => { const navigateToTop = useNavigateToTop(); const navigateAndReset: MouseEventHandler<HTMLAnchorElement> = (event) => { event.preventDefault(); navigateToTop(props.to); }; return ( <Link className={props.className} onClick={navigateAndReset} to={props.to}> {props.children} </Link> ); }; import { useNavigate } from "react-router-dom"; /** Navigate to the top of a page so that the scroll position isn't persisted between pages. Use this instead of React Dom's build-in @see {@link useNavigate}. */ export const useNavigateToTop = () => { const navigate = useNavigate(); const navigateAndReset = (to: string) => { navigate(to, { replace: true }); window.scrollTo(0, 0); }; return navigateAndReset; };