為你的 React Router 包裝 Navigate
tags: React
category: Front-End
description: 為你的 React Router 包裝 Navigate
created_at: 2022/05/23 19:30:00
前言
這邊所指的 React Router
指的是 v6
的版本,在這個版本中多了一個叫做 navigate
的東西去幫助你做轉址之類的操作。
而為什麼已經有 useNavigate
這個 hook
,還需要再做包裝呢?
因為他的定義像是這個樣子
declare function useNavigate(): NavigateFunction;
interface NavigateFunction {
(
to: To,
options?: { replace?: boolean; state?: any }
): void;
(delta: number): void;
}
簡單來說他就是支援兩種操作
- 給我
你要去哪裡(to)
跟額外參數(options)
,然後幫你轉過去 - 只給我一個數字,(通常是用在上一頁 填
-1
)
而這兩種操作雖然看起來很棒,嗯他們確實很棒,但是不夠我用啊!!,我需要路由參數的話怎麼辦? 我又不想要乖乖組字串
所以才自己想額外包裝一個使用的方式,增加自己開發的體驗。
定義需求
因為在 React Router
中,路由的用法大概長得像下面這樣(不論有無巢狀)。
<Routes>
<Route path="/" element={<Home />} />
<Route path="/users/:id" element={<UserPage />} />
<Route path="/other" element={<OtherPage />} />
</Routes>
因為我們關注的點在於那個 path
,所以跟巢狀沒有太大的關係。
所以假設以目前官方提供的方式,我希望轉入 users/:id
這個頁面,我必須這樣做
import { useNavigate } from 'react-router-dom'
// ...
const navigate = useNavigate()
navigate('/users/1')
// ...
而那個數字如果是動態的我該怎麼辦呢? 可能只能採取下面的作法
const user = { id: 1 } // <-- 假設我從其他地方來,例如 API
navigate('/users/' + user.id) // <-- 原始的字串+法
navigate(`/users/${user.id}`) // <-- ES6的模板字串
這時可能有些人會對 navigate
的使用方式有疑問,上面提到了還有第二個參數 options
裡面有個 { state: }
不是也可以帶資料嗎(?)
對,那個也可以帶資料,但是不會影響到網址上面,也就是如果你把網址貼給別人沒辦法重現一樣的行為 (?)
所以我希望可以做到這件事
const navigate = useParamNavigate() // <-- 自己寫的 hook
// 希望可以藉由以下方式切換路由參數
navigate('/users/:id', { id: 1})
navigate('/users/:id', { id: 2})
navigate('/users/:id', { id: 3})
如果透過這樣的方式去轉址,不管前面的網址在複雜,你用起來都快樂很多。
假設我這邊用不到那個 options
的功能,所以我先不 forward
他,如果自己有需求再自己加。
開始包裝
包裝前當然你要有一個 React
專案,所以還沒有的自己建一下
$ npx create-react-app <project-name>
or
$ yarn create react-app <project-name>
首先你還必須安裝套件,基本上可以參考官方: https://reactrouter.com/docs/en/v6/getting-started/installation
但我這邊就貼比較常用的方式
$ npm install react-router-dom@6
or
$ yarn add react-router-dom@6
然後再次順便廣告一下,如果要快速幫你裝好像是 Eslint
、Prettier
、Tailwindcss
之類的環境,可以考慮我做的 cmd
: https://github.com/LaiJunBin/lai-cmd
都安裝好之後終於可以開始了,首先建立一個檔案,假設我叫做 useParamNavigate.js
import 'core-js/features/string/match-all' // <-- 如果加這行需要額外安裝套件,可參考下方說明
import { useNavigate } from 'react-router-dom'
const useParamNavigate = () => {
const navigate = useNavigate() // <-- 使用原來的 navigate
return (path = '/', params = {}) => { // <-- 這邊是要回傳回去給程式使用的 function
for (const [k, v] of path.matchAll(/:([^/]+)/g)) { // <-- 使用正規表達式抓出路由參數
if (!params[v]) { // <-- 如果需要的參數不在,可以自己處理
return navigate('/404') // <-- 我這邊是直接丟去404
}
path = path.replace(k, params[v]) // <-- 取代 path
}
navigate(path) // 最後做轉址
}
}
export default useParamNavigate
這樣就完成了,至於第一行的 import 'core-js/features/string/match-all'
,有興趣可以參考我上一篇文章的
關於 Polyfill。