Back to Blog

Scroll Restoration

Paling rapi dari beberapa implementasi scroll restoration yang pernah saya terapkan. Syaratnya: context + ref

Fergus Hagaswara

1/15/2023

Artikel singkat ini akan membahas bagaimana mengimplementasikan scroll restoration pada project React dan NextJS.

Context Provider

Hal pertama yang harus dilakukan adalah membuat provider untuk scroll restoration. Saya akan buat di src/providers/ScrollProvider

/*
src/providers/ScrollProvider/ScroProvider.tsx
*/

import {createContext, MutableRefObject, ReactNode, useContext, useEffect, useRef} from 'react'

type AppContextType = {
  scrollRef: MutableRefObject<{scrollPos: number}>
}

const AppContext = createContext<AppContextType | undefined>(undefined)

const ScrollProvider = ({children}: {children: ReactNode}) => {
  const scrollRef = useRef({
    scrollPos: 0
  })

  return <AppContext.Provider value={{scrollRef}}>{children}</AppContext.Provider>
}

const useProvider = () => {
  const context = useContext(AppContext)

  if (context === undefined) {
    throw new Error('You are using the hook outside of the scroll provider')
  }

  useEffect(() => {
    window.scrollTo(0, context.scrollRef.current.scrollPos)
    const handleScrollPos = () => {
      context.scrollRef.current.scrollPos = window.scrollY
    }

    window.addEventListener('scroll', handleScrollPos)

    return () => {
      window.removeEventListener('scroll', handleScrollPos)
    }
  }, [])

  return context
}

export {ScrollProvider, useProvider}

kemudian entry point untuk provider tadi

/*
src/providers/ScrollProvider/index.ts
*/

import {ScrollProvider, useProvider} from './ScrollProvider'

export default ScrollProvider
export {useProvider as useScrollRestoration}

Pemakaian

Gunakan <ScrollProvider> di entry point project anda sebelum komponen lainnya di render.

untuk NextJS di pages/_app.tsx

const MyApp = ({Component, pageProps}: AppProps) => {
  return (
    <ScrollProvider>
      <Component {...pageProps} />
    </ScrollProvider>
  )
}

untuk CRA di src/index.tsx

root.render(
  <React.StrictMode>
    <ScrollProvider>
      <App />
    </ScrollProvider>
  </React.StrictMode>
)

dan import useScrollRestoration di komponen yang membutuhkan scroll restoration

import React from 'react'
import {useScrollRestoration} from 'src/providers/ScrollProvider'

const PageWithLongContent = () => {
  useScrollRestoration()
  
  return (
    //long scrollable content
  )
}

Untuk NextJS jangan lupa menambahkan props scroll={false} di setiap <Link> yang menuju halaman yang menggunakan useScrollRestoration(). Tujuannya agar router NextJS tidak menjalankan default behaviour nya yaitu otomatis scroll ke paling atas halaman saat melaukan navigasi.

Kesimpulan

Menurut saya implementasi scroll restoration ini adalah yang paling efisien dan rapi dari beberapa implementasi yang pernah saya terapkan. Tidak membutuhkan package tambahan, tidak menimbulkan masalah performa karena menggunakan ref untuk menyimpan posisi scroll terakhir, dan tidak menambahkan banyak kode-kode boilerplate pada pemakaiannya seperti yang sudah saya contohkan di atas.

Yang perlu diperhatikan adalah kemungkinan besar scroll restoration ini kurang memadai untuk menangani routing yang kompleks. Sebagai contoh perlu ditambahkan fungsionalitas untuk melakukan mapping posisi scroll untuk masing-masing halaman, sehingga posisi scroll terakhir yang akan di restore lebih presisi dan tidak saling tercampur.

Referensi Tambahan