📆 Thời gian:
🕐 3 phút đọc
Trong khi làm việc trên dự án tiếp theo đầu tiên của tôi và kiểm tra nó từ góc độ người dùng, tôi thấy nó hơi khó chịu, rằng khi sử dụng nút quay lại của trình duyệt, người dùng luôn được đưa lên đầu trang thay vì vị trí mà họ tiếp tục chuyến trang. Sau khi thực hiện một số nghiên cứu và không tìm thấy một giải pháp thỏa mãn, tôi quyết định tự tạo ra một giải pháp.

Đầu tiên ta tạo một Custom Hook, và nó phải được gọi một lần trong dự án của bạn. (Code này viết bằng Typescript nên đặt tên file là usePreserveScroll.ts, khi import phải có đuôi .ts )
import { useRouter } from "next/router"
import { useEffect, useRef } from "react"
export const usePreserveScroll = () => {
const router = useRouter()
const scrollPositions = useRef<{ [url: string]: number }>({})
const isBack = useRef(false)
useEffect(() => {
router.beforePopState(() => {
isBack.current = true
return true
})
const onRouteChangeStart = () => {
const url = router.pathname
scrollPositions.current[url] = window.scrollY
}
const onRouteChangeComplete = (url: any) => {
if (isBack.current && scrollPositions.current[url]) {
window.scroll({
top: scrollPositions.current[url],
behavior: "auto",
})
}
isBack.current = false
}
router.events.on("routeChangeStart", onRouteChangeStart)
router.events.on("routeChangeComplete", onRouteChangeComplete)
return () => {
router.events.off("routeChangeStart", onRouteChangeStart)
router.events.off("routeChangeComplete", onRouteChangeComplete)
}
}, [router])
}Sau đó ta tiến hành import Custom Hook vào _app.tsx / _app.js dự án của bạn.
import { usePreserveScroll } from "./usePreserveScroll.ts";
function MyApp({ Component, pageProps: { session, ...pageProps } }) {
usePreserveScroll();
.....
}const router = useRouter()
const scrollPositions = useRef<{ [url: string]: number }>({})
const isBack = useRef(false)useRouter() cho phép truy cập vào bộ định tuyến tích hợp sẵn của Next.js
scrollPositions là một object, sẽ nhận các key là URL đã truy cập và value là vị trí scroll tương ứng.
isBack sẽ được đặt thành true, khi một điều hướng thông qua browser-back-button (nút back trình duyệt).
Cả 2 scrollPositions và isBack đều sử dụng useRef-hook để đảm bảo vẫn giữ được giá trị sau mỗi lần re-render.
useEffect(() => {
...
}, [router])Khi router thay đổi, hàm useEffect sẽ được thực thi
router.beforePopState(() => {
isBack.current = true
return true
})router.beforePopState sẽ được gọi khi có popstate event xảy ra (như sử dụng nút back của trình duyệt), có thể đọc thêm tại đây.
Khi đó, isBack sẽ mang giá trị true, return true, do đó Nextjs sẽ tiếp tục xử lý phần còn lại của popstate.
const onRouteChangeStart = () => {
const url = router.pathname
scrollPositions.current[url] = window.scrollY
}Khi route bắt đầu thay đổi, thì scrollPositions object sẽ lưu giá trị vị trí scroll và key là url của trang hiện tại, sau đó mới tiến hành chuyển đến route đó.
const onRouteChangeComplete = (url: any) => {
if (isBack.current) {
window.scroll({
top: scrollPositions.current[url],
behavior: "auto",
})
}
isBack.current = false
}Khi route thay đổi thành công, sẽ tiến hành kiểm tra isBack, nếu isBack là true thì sẽ tiến hành quay lại vị trí scroll đã lưu ở object scrollPositions. Ở đây behavior được set là “auto” để đảm bảo việc scroll lại tức thì và hiệu ứng chuyển scroll sẽ không được nhìn thấy.

Tham chiếu: https://jak-ch-ll.medium.com/next-js-preserve-scroll-history-334cf699802a
0
0
0
0
nextjs, scroll nextjs, preserve scroll history, scroll history nextjs, khôi phục vị trí scroll nextjs, cách khôi phục scroll nextjs