Next.js-Router Hooks

到了 SSR 以後就會需要用到它。

簡述

在 SSR 的世界裡面實作「Loading」效果的方式跟 CSR 不太一樣。

想想看在 CSR 中你是怎麼做的?沒意外的話應該是這樣吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function SomePage() {
const [loading, setLoading] = useState(true)

useEffect(() => {
fetch(...)
.then((response) => response.json())
.then((data) => {
console.log('data', data)
setLoading(false)
})
.catch((error) => {
console.log('error', error.message)
setLoading(false)
})
}, [])

return loading ? (
<div>Loading...</div>
) : (
<div>Lorem ipsum</div>
)
}

邏輯就是把一開始 fetch 拿資料做初始化的這段時間設為 loading 效果,等拿到結果後在更新 state。

But 在 SSR 裡面不會有 fetch 這件事,因為資料在 Server 端時就會渲染完了,所以等到元件渲染時資料早就已經處理完畢了,沒有機會讓你顯示 Loading 的狀態。

不過 Server 在渲染的這段期間確實是需要等待的,只是要換種方式來實作,其中一種方式就是透過「路由事件」來判定。

useRouter 除了提供基本路由操作以外,還有以下事件可以用:

  • routeChangeStart 路由開始跳轉時觸發
  • routeChangeComplete 路由結束跳轉時觸發
  • routeChangeError 路由跳轉發生錯誤時觸發
  • beforeHistoryChange 歷史紀錄改變時觸發
  • hashChangeStart “#” 後的路徑開始改變時觸發
  • hashChangeComplete “#” 後的路徑開始改變時觸發

詳細介紹可以參考 官方文件,不過最常用的基本上是 routeChangeStartrouteChangeComplete

有了這個 event 後要實作 loading 就很輕鬆了,只要在 Layout 的部分根據事件來做出反應就行了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'

export default function Layout({ children }) {
const router = useRouter()
const [loading, setLoading] = useState(false)

useEffect(() => {
function onRouteChangeStart() {
console.log('routeChangeStart')
setLoading(true)
}

function onRouteChangeComplete() {
console.log('routeChangeComplete')
setLoading(false)
}

function onRouteChangeError() {
console.log('onRouteChangeError')
alert('Ooops! something goes wrong.')
setLoading(false)
}

router.events.on('routeChangeStart', onRouteChangeStart)
router.events.on('routeChangeComplete', onRouteChangeComplete)
router.events.on('routeChangeError', onRouteChangeError)

// 以這個例子來說這邊不需要清除 Effect,不過還是養成好習慣比較好。
return () => {
console.log('clear handler')
router.events.off('routeChangeStart', onRouteChangeStart)
router.events.off('routeChangeComplete', onRouteChangeComplete)
router.events.off('routeChangeError', onRouteChangeError)
}
}, [])

return (
<div className='layout'>
<div className='page-content'>{loading ? <div>Loading...</div> : children}</div>
</div>
)
}
Next.js-使用圖片 Next.js-重新導向(router)
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×