Ant Design-Breadcrumb

突然懷念起以前手刻麵包屑的時光。

最基本的結構

基本結構會由 <Breadcrumb><Breadcrumb.Item> 來組成。

<Breadcrumb.Item> 裡面可以放我們想放的元件,例如 <a><Link>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React from 'react'
import { Breadcrumb } from 'antd'

function App() {
return (
<>
<h2>Bread crumb</h2>
<Breadcrumb>
<Breadcrumb.Item>Home</Breadcrumb.Item>
<Breadcrumb.Item>
<a href='#'>App</a>
</Breadcrumb.Item>
<Breadcrumb.Item>
<a href='#'>List</a>
</Breadcrumb.Item>
<Breadcrumb.Item>
<a href='#'>Detail</a>
</Breadcrumb.Item>
</Breadcrumb>
</>
)
}

export default App

輸出結果:

bread-crumb-basic

因為是麵包屑,所以最後一個 item 會自動加上 active 的樣式(最後一個等於目前所在位置),不用自己做處理。

指定間隔符

可以用 separator 這個屬性來處理:

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
import React from 'react'
import { Breadcrumb } from 'antd'

function App() {
return (
<>
<h2>Bread crumb</h2>
{/* 指定為 > */}
<Breadcrumb separator='>'>
<Breadcrumb.Item>Home</Breadcrumb.Item>
<Breadcrumb.Item>
<a href='#'>App</a>
</Breadcrumb.Item>
<Breadcrumb.Item>
<a href='#'>List</a>
</Breadcrumb.Item>
<Breadcrumb.Item>
<a href='#'>Detail</a>
</Breadcrumb.Item>
</Breadcrumb>
</>
)
}

export default App

當然也可以放入元件:

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
import React from 'react'
import { Breadcrumb } from 'antd'
import { CaretRightOutlined } from '@ant-design/icons'

function App() {
return (
<>
<h2>Bread crumb</h2>
{/* 放入 icon 元件 */}
<Breadcrumb separator={<CaretRightOutlined />}>
<Breadcrumb.Item>Home</Breadcrumb.Item>
<Breadcrumb.Item>
<a href='#'>App</a>
</Breadcrumb.Item>
<Breadcrumb.Item>
<a href='#'>List</a>
</Breadcrumb.Item>
<Breadcrumb.Item>
<a href='#'>Detail</a>
</Breadcrumb.Item>
</Breadcrumb>
</>
)
}

export default App

輸出結果:

bread-crumb-separator

與 React 路由做連結

這個我覺得剛開始碰的話有點小複雜,所以不太懂的話建議跟著實作看看。

先來看一下最後的成果:

bread-crumb-component-style

主要的思路是這樣:

  1. 取得目前所在的路徑
  2. 把路徑拆開來,例如 /app/list, 會被拆成 ['app', list]
  3. 利用第二步的陣列來產生網址 ['app', 'list'] => /app/app/list
  4. 建立一個 map,一個網址會對應到一個名稱(用來顯示在麵包屑上的)
  5. 利用上面產生的「網址」和「map」來產生麵包屑元件

接著就來看 code 吧:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import React from 'react'
import { Breadcrumb } from 'antd'
import { CaretRightOutlined } from '@ant-design/icons'
import { Routes, Route, Link, Outlet, useLocation } from 'react-router-dom'
import Home from './Home'
import App from './App'
import List from './List'
import Detail from './Detail'

interface NameMap {
[key: string]: string
}

// 用來做映射的 map
const breadcrumbNameMap: NameMap = {
'/app': 'App',
'/app/list': 'List',
'/app/list/detail': 'Detail'
}

function Main() {
const location = useLocation()

// 抓出目前的路徑(首頁的話會是空字串,所以會用 filter 過濾掉)
const pathSnippets = location.pathname.split('/').filter((i) => i)

// 根據路徑來產生需要的元件
const extraBreadcrumbItems = pathSnippets.map((_, index) => {
// 這邊的邏輯要想一下,可以看成這樣子:
// 第一個麵包屑的連結是 /app,所以 = ['app', 'list'].slice(0, 1).join('/')
// 第二個麵包屑的連結是 /app/list,所以 = ['app', 'list'].slice(0, 2).join('/')
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`
// 最後把值填到麵包屑元件中(別忘了加上 key)
return (
<Breadcrumb.Item key={url}>
<Link to={url}>{breadcrumbNameMap[url]}</Link>
</Breadcrumb.Item>
)
})

// 首頁 + 上面產生的麵包屑(因為是陣列,所以別忘了加上 key)
const breadcrumbItems = [
<Breadcrumb.Item key={'Home'}>
<Link to=''>Home</Link>
</Breadcrumb.Item>
].concat(extraBreadcrumbItems)

return (
<>
<Breadcrumb
style={{
marginBottom: '30px'
}}
separator={<CaretRightOutlined />}
>
{/* 最後把產生好的 item 放進來就可以了 */}
{breadcrumbItems}
</Breadcrumb>

<Routes>
<Route path='' element={<Outlet />}>
<Route path='' element={<Home />} />
<Route path='app' element={<Outlet />}>
<Route path='' element={<App />} />
<Route path='list' element={<Outlet />}>
<Route path='' element={<List />} />
<Route path='detail' element={<Detail />} />
</Route>
</Route>
</Route>
</Routes>
</>
)
}

export default Main

至於路由的部分看不太懂的話,可以參考我之前寫的這篇

另一種做法,抽出去當成一個元件

剛剛的做法其實有個問題,就是沒辦法處理「動態路由」的 mapping。

回顧一下,當時 map 的部分我們是這樣寫的:

1
2
3
4
5
6
// 用來做映射的 map
const breadcrumbNameMap = {
'/app': 'App',
'/app/list': 'List',
'/app/list/detail': 'Detail'
}

問題在於,如果現在網址是 /app/list/:id 的話,map 要怎麼寫?好像就沒辦法了,雖然硬要的話是可以寫成這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const breadcrumbNameMap = {
'/app': 'App',
'/app/list': 'List'
}

// 擷出路徑片段
const pathSnippets = location.pathname.split('/').filter((i) => i)

// 檢查最後一個片段是不是數字
if (parseInt(pathSnippets[pathSnippets.length - 1], 10)) {
// 加上動態路由的 map
for (let i = 1; i <= parseInt(pathSnippets[pathSnippets.length - 1], 10); i++) {
breadcrumbNameMap[`/app/list/${i}`] = `List of ${i}`
}
}

附註:有興趣的話可以到這邊參考範例

雖然這也不失為一種做法,不過如果想更有彈性一些的話,可以試著用「抽出去變成元件」這種思維來做。

這會有點類似 HOC 的感覺。簡單來說,我們可以自製一個元件,它會接收一個 props,這個 props 要包含這些資訊:

1
2
3
4
5
type Paths = {
url: string // 網址
labal: string // 顯示在麵包屑上的文字
access: boolean // 用來決定要顯示連結 or 純文字
}

而這元件就會根據 props 來產生對應的麵包屑元件:

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
import { Breadcrumb } from 'antd'
import { Link } from 'react-router-dom'
import React from 'react'

export type Paths = {
url: string
label: string
access: boolean
}

interface Props {
paths: Paths[]
}

const PageBreadcrumb: React.FC<Props> = ({ paths }) => {
// 根據 props 產生麵包屑 item
const Breadcrumbitems = paths.map((path) => {
return (
<Breadcrumb.Item key={path.url}>
{path.access ? <Link to={path.url}>{path.label}</Link> : path.label}
</Breadcrumb.Item>
)
})
// 把做好的麵包屑回傳
return (
<Breadcrumb
style={{
marginBottom: '30px'
}}
>
{Breadcrumbitems}
</Breadcrumb>
)
}

export default PageBreadcrumb

接著只要在對應的元件上使用就行了:

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
import React from 'react'
import { Link } from 'react-router-dom'
import PageBreadcrumb, { Paths } from '../PageBreadcrumb'

const List: React.FC = () => {
const breadcrumpPaths: Paths[] = [
{
url: '/',
label: 'Home',
access: true
},
{
url: '/app',
label: 'App',
access: true
},
{
url: '/app/list',
label: 'List',
access: true
}
]
return (
<>
<PageBreadcrumb paths={breadcrumpPaths} />
<ul>
{Array.from({ length: 10 }, (_, index) => {
return (
<li key={index}>
<Link to={(index + 1).toString()}>List item {index + 1}</Link>
</li>
)
})}
</ul>
</>
)
}

export default List

這樣的缺點是每一個頁面都得各別處理麵包屑元件,但換來的好處是可以有更大的彈性。總之我覺得要用哪一種方法都可以,端看你偏好哪個,最後一樣附上範例

Ant Design-表單相關元件 Ant Design-Table
Your browser is out-of-date!

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

×