Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Scroll restoration listeners are never removed, so every created router stays in memory

Aperta
#8,775 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
42/100
Tipo di issue
Funzionalità
Chiarezza
Abbastanza chiara
Stato di attività
Attiva
Stack tecnologico
typescript
Ambito
frontend

Direzione di ricerca

Start with packages/router-core/src/scroll-restoration.ts and the RouterCore constructor, where setup is called; trace the listeners and router subscriptions that need cleanup. Then inspect existing router lifecycle tests and add coverage for repeated dispose() calls and multiple routers sharing scroll restoration. Done means disposing a router removes its listeners and subscriptions without changing history.scrollRestoration while another router still uses it.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Which project does this relate to?

Router

Describe the bug

When scrollRestoration is on, setupScrollRestoration (packages/router-core/src/scroll-restoration.ts) runs once per router, from the RouterCore constructor. It adds three global listeners:

  • document.addEventListener('scroll', …, true)
  • addEventListener('pagehide', …)
  • addEventListener('pageshow', …)

It keeps no reference to them, and the router has no method to remove them. Each listener closes over the router, so the router (its stores, matches, and the elements in trackedScrollTargets) cannot be garbage collected.

With one router for the life of the page this is fine. It is a leak when an app creates routers more than once, for example:

  • an app that renders some views in their own router instance and creates one per view (our case: each page in a stack of sliding panels has its own router, so the page under a panel stays mounted)
  • micro-frontends that mount and unmount a router
  • tests and HMR that create a new router each time

In our app, after opening and closing 20 panels, getEventListeners(document).scroll.length goes from 1 to 21. Each pagehide listener also writes the scroll cache to sessionStorage, so N routers write it N times on unload.

Steps to reproduce
import { createMemoryHistory, createRootRoute, createRouter } from '@tanstack/react-router'

for (let i = 0; i < 20; i++) {
  createRouter({
    routeTree: createRootRoute(),
    history: createMemoryHistory(),
    scrollRestoration: true,
  })
}

// Chrome DevTools console:
getEventListeners(document).scroll.length // 20
getEventListeners(window).pagehide.length // 20
Expected behavior

There is a way to release a router that is no longer used: its global listeners are removed and it can be garbage collected.

Proposed fix
  • setupScrollRestoration keeps the listeners (and the router subscriptions it adds), and returns a cleanup function.
  • RouterCore stores that cleanup and exposes it as a public method, for example router.dispose(). It is idempotent and does not touch history.scrollRestoration if another router still uses scroll restoration.
  • RouterProvider does not call it automatically, because the app creates the router outside React and may render it again.

I can open a PR with this and tests. Is dispose() an acceptable name and shape, or do you prefer something else (for example destroy(), or a cleanup returned from router.mount())?

Your environment
  • @tanstack/react-router 1.170.41 (also present on main, see scroll-restoration.ts)
  • Chrome 155, macOS

Related: #8024 (bare addEventListener for pagehide), #7956 (history.scrollRestoration mode).

Lingua principale
TypeScript
Stelle
15.2k
Fork
1.9k
Merge medio
1g 13h
PR unite (30g)
76

Preparare l'ambiente

Apri in Codespaces

Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di TanStack/router

Tutte le issue di TanStack/router

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.