# [Udemy - React] 課程流程筆記(sec. 20 - Router) ###### tags: `Udemy 課程筆記` `React` `前端筆記` ## 269. Defining Routes > 就像是寫 component 一樣 1. 建立 router config - router config 就是一個 mapping,每一組 property 都有 `path` 及對應渲染的 `element`(component) - `createBrowserRouter()` 2. 在專案接口中告知 react 要使用 router config,以便根據 `path` 顯示不同的 component - `<RouterProvider router={建立的 router config}></RouterProvider>` 3. 常會有 `pages/` directory 放與 `path` 對應的 component ``` src/ pages/ -> 依據 path 顯示的 components Home/ components/ -> 共用的 components ``` 4. `path` 是指 routing 的 `path` ``` https://example/product -> http protocol//domain name//path ``` ## 271. Exploring an Alternative Way of Defining Routes > 結果這個更像在寫 JSX compoment XD ```javascript! // 更像寫 JSX component 的方式 XD const router = createBrowserRouter(createRoutesFromElements( <Route> <Route path={PATH_MAP.HOME} element={<Home />}></Route> <Route path={PATH_MAP.PRODUCT} element={<Product />}></Route> </Route> )) ``` ## Navigate ### 透過 template 更換 routing #### 只要跳轉就好,不用 UI 顯示提示 ```javascript! // component... // ... return ( <Link to={'some path'}>Go to some path</Link> ) // ... ``` #### 要透過 UI 告知使用者當前哪個 path 被選擇 `<NavLink>` 有 callback,其 parameter 有 `{ isActive: boolean }`,開發者可以透過這個 parameter 得知當前 path 是否吻合: ```javascript! // component... // ... return ( <NavLink to={'some path'} className={({ isActive }) => isActive ? 'active' : ''} > Go to some path </NavLink> ) // ... ``` 預設只要 path 前綴吻合就會認為當前 path 吻合,這會導致 children 的 path 吻合時 parent 也會被判定為吻合,如果想要讓 parent 不會因為前綴吻合就被判定 active 的話可以在 parent path 加上 `end`: ```javascript! // component... // ... return ( <NavLink to={'/events'} className={({ isActive }) => isActive ? 'active' : ''} end // 避免當前 path 為 /events/new 時,這裡的 path 會被認作 active(因為預設是前綴一樣就是 active) > Go to events </NavLink> <NavLink to={'/events/new'} className={({ isActive }) => isActive ? 'active' : ''} > </NavLink> ) // ... ``` ## 279. Understanding Relative & Absolute Paths 只要以 `/ (slash)` 開頭就會被認為是 absolute path(絕對路徑),因此如果要跳轉的話就要寫完整的 path: ```javascript! // router config map // import stuff ... const router = createBrowserRouter([ { path: '/', element: <RootLayout />, children: [ { index: true, // 也可以寫 path: '',但 index: true 可以更加語義化地顯示這個是 default path element: <Home /> }, { path: 'events', element: <EventRoot />, children: [ { index: true, element: <Events /> }, { path: ':eventId' index: truEventDetail }, ] } ] } ]) ``` ```javascript! // component ... // currentPath: ../events // ... return ( // 以 slash 為開頭就是 absolute path,因此如果想要跳轉就必須把全部的 path 寫完整(會從 root path 新增 path) <Link to={'/events/1'}>Go to Event 1</Link> <Link to={'/events/2'}>Go to Event 2</Link> ) // ... ``` 如果不是以 `/ (slash)` 開頭的話就是 relative path(相對路徑),`react-router-dom` 會十分聰明地直接在當前 path 新增要跳轉的 path: ```javascript! // component ... // currentPath: ../events // ... return ( // 被渲染成 DOM 會是 <a href="/events/1">Go to Event 1/a>,因為沒有以 / 開頭,所以會自動往後加 path <Link to={'1'}>Go to Event 1</Link> <Link to={'2'}>Go to Event 2</Link> ) // ... ``` ## 283. Time to Practice: Solution ### 將 path 獨立成一個 map,並透過函式實現動態塞 params ```javascript! const PATH_MAP = { HOME: () => '/', PRODUCT: () => 'product', PRODUCT_DETAIL: (productId) => `product/${productId}` } export default PATH_MAP ``` `path: string`,原本要插 `params` 時必須寫成 `product/:productId`,只要 `:propertName` 就會被認作 `params`。因為這邊使用 function 建立 `path`,所以只要讓 function 回傳的 `string` 有包含 `:` 就可以了: ```javascript! // router/index.js import PATH_MAP from './PATH_MAP' const router = createBrowserRouter([ { path: PATH_MAP.HOME(), element: ..., children: [ { path: PATH_MAP.PRODUCT(), element: ..., children: [ { path: PATH_MAP.PRODUCT_DETAIL(':productId'), // 定義的函式會收 parameter 並回傳 product/:productId,告知 react-router-dom 這裡有一個 params element: ... } ] } ] } ]) ``` ## 要訪問內層巢狀的 component 必須多開一個 `<Outlet />` ```javascript! const router = createBrowserRouter([ { path: '/', element: <Layout />, children: [ { path: 'product', element: <RootProduct />, // 第一層 children: [ { path: ':id', element: <ProductDetail /> // 第二層 } ] } ] } ]) ``` ```javascript! const Layout = () => { return ( <> <Outlet /> // 只會渲染第一層符合的 </> ) } const RootProduct = () => { return ( <> <Outlet /> // 這個負責第二層的渲染,當 path 吻合第二層的設定時會把 component 渲染在這邊 </> ) } ```