到了 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
“#” 後的路徑開始改變時觸發
詳細介紹可以參考 官方文件,不過最常用的基本上是 routeChangeStart
和 routeChangeComplete
。
有了這個 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)
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> ) }
|