Ant Design-Menu

網頁中最重要的一部分。

基本結構

附註:如果是 <= 4.20 的話得用 JSX 的寫法

會由 MenuMenu.ItemMenu.SubMenuMenu.ItemGroup 來組成:

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, { useState } from 'react'
import { Menu } from 'antd'

function Main() {
return (
<Menu mode='horizontal' defaultSelectedKeys={['home']}>
<Menu.Item key='home'>Home</Menu.Item>
<Menu.Item key='about'>About</Menu.Item>
<Menu.SubMenu key='products' title='Products'>
<Menu.ItemGroup title='Type A'>
<Menu.Item key='a1'>ProductA-1</Menu.Item>
<Menu.Item key='a2'>ProductA-2</Menu.Item>
<Menu.Item key='a3'>ProductA-3</Menu.Item>
</Menu.ItemGroup>
<Menu.ItemGroup title='Type B'>
<Menu.Item key='b1'>ProductB-1</Menu.Item>
<Menu.Item key='b2'>ProductB-2</Menu.Item>
<Menu.Item key='b3'>ProductB-3</Menu.Item>
</Menu.ItemGroup>
</Menu.SubMenu>
<Menu.Item key='contact'>Contact</Menu.Item>
</Menu>
)
}

export default Main

出來的結果大概就像這樣

antd-menu

如果是 >= 4.20 的話,可以改用 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
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
import { Menu } from 'antd'

const items = [
{
key: 'home',
label: 'Home'
},
{
key: 'about',
label: 'About'
},
{
key: 'producrs',
label: 'Products',
// submenu
children: [
// group1
{
type: 'group',
label: 'Type A',
children: [
{
key: 'a1',
label: 'ProductA-1'
},
{
key: 'a2',
label: 'ProductA-2'
},
{
key: 'a3',
label: 'ProductA-3'
}
]
},
// group2
{
type: 'group',
label: 'Type B',
children: [
{
key: 'b1',
label: 'ProductB-1'
},
{
key: 'b2',
label: 'ProductB-2'
},
{
key: 'b3',
label: 'ProductB-3'
}
]
}
]
},
{
key: 'service',
label: 'Service'
}
]

export default function App() {
return <Menu items={items} mode='horizontal' defaultSelectedKeys={['home']} />
}

至於 active 的狀態它會自己幫你處理,所以不用額外設定。

自己處理 active 狀態

如果你想要自己設定 active 狀態的話,可以用 selectedKeys 來處理:

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

function Main() {
const [current, setCurrent] = React.useState<string[]>(['home'])

const handleClick: MenuProps['onClick'] = (e) => {
// 顯示點擊項目的 key(我們設的那個)
console.log('click', e.key)
setCurrent([e.key])
}

return (
<Menu mode='horizontal' onClick={handleClick} selectedKeys={current}>
<Menu.Item key='home'>Home</Menu.Item>
<Menu.Item key='about'>About</Menu.Item>
<Menu.SubMenu key='products' title='Products'>
<Menu.ItemGroup title='Type A'>
<Menu.Item key='a1'>ProductA-1</Menu.Item>
<Menu.Item key='a2'>ProductA-2</Menu.Item>
<Menu.Item key='a3'>ProductA-3</Menu.Item>
</Menu.ItemGroup>
<Menu.ItemGroup title='Type B'>
<Menu.Item key='b1'>ProductB-1</Menu.Item>
<Menu.Item key='b2'>ProductB-2</Menu.Item>
<Menu.Item key='b3'>ProductB-3</Menu.Item>
</Menu.ItemGroup>
</Menu.SubMenu>
<Menu.Item key='contact'>Contact</Menu.Item>
</Menu>
)
}

export default Main

跟 React router 結合起來

如果可以透過一份資料,就自動產生 Menu 跟 Router 的話是不是挺方便的?這個其實是有辦法做到的!這邊會先介紹 >= 4.20 的方法,比較簡潔一點。

假設 Menu 會有四個連結:

  • Home
  • Products
  • Service
  • About

那導覽列的部分就會這樣寫:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const menu = [
{
key: 'home',
label: 'Home'
},
{
key: 'products',
label: 'Products'
},
{
key: 'service',
label: 'Service'
},
{
key: 'about',
label: 'About'
}
]


fucntino Nav () {
return <Menu items={menu} />
}

路由的部分則會這樣寫:

1
2
3
4
5
6
7
8
9
10
function Main() {
return (
<Routes>
<Route path='home' element={<Home />} />
<Route path='products' element={<Products />} />
<Route path='service' element={<Service />} />
<Route path='about' element={<About />} />
</Routes>
)
}

好,接下來可以思考一件事:

有沒有辦法讓 menu 也能渲染 router?

你可以先想想看如果要讓 router 也能共用的話,還需要哪些資料?

想完後再拉下去看,最後會整理成像這樣:

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
import { Link } from 'react-router-dom'
import Home from './Home'
import About from './About'
import Prodcuts from './Prodcuts'
import Service from './Service'

const menu = [
{
key: 'home',
path: '',
label: <Link to=''>Home</Link>,
component: <Home />
},
{
key: 'products',
path: 'products',
label: <Link to='products'>Products</Link>,
component: <Products />
},
{
key: 'service',
path: 'service',
label: <Link to='service'>Service</Link>,
component: <Service />
},
{
key: 'about',
path: 'about',
label: <Link to=''>About</Link>,
component: <About />
}
]

首先需要讓導覽列上的按鈕可以連結到路由,所以把 label 的部分改成 <Link> 的形式。(如果希望不要有連結,可以保留為純文字就好)

接著 router 的部分因為要渲染出 <Route path="path" element={<Component />} /> 的形式,所以加上 pathcomponent 的屬性。

資料 OK 以後,接著只要把渲染函式寫出來就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 資料
import { menu } from './menu'

// 渲染函式
function renderRoute(routes) {
return routes.map((route) => (
<Route key={route.key} path={route.path} element={route.component} />
))
}

function RouteComponent() {
return <Routes>{renderRoute(menu)}</Routes>
}

做到這邊以後,就可以透過 menu 來自動生成導覽列跟路由了,想看範例的話到 這邊 看。

<= 4.20 的做法

如果是 <=4.20 的話,<Menu /> 不能用 items 屬性,所以就要自己寫一個 generator 來產生:

1
2
3
4
5
6
7
8
9
function Nav() {
return (
<Menu>
{menu.map((menu) => (
<Menu.Item key={menu.key}>{menu.label}</Menu.Item>
))}
</Menu>
)
}

也沒有很複雜,只是順便提一下。

子路由與子導覽列

這邊只是做個補充,基本概念都跟上面一樣,只是資料更多層,還有渲染函式稍微複雜一點:

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
// Nav.tsx
import React from 'react'
import { Menu } from 'antd'
import { Link, Outlet } from 'react-router-dom'
import Home from '../pages/Home'
import ProdcutA from '../pages/ProductA'
import ProdcutB from '../pages/ProductB'

export const items = [
{
key: 'home',
url: '',
label: <Link to=''>Home</Link>,
component: <Home />
},
{
key: 'products',
url: 'products',
label: 'Products',
component: <Outlet />,
// 子路由 & 子導覽
children: [
{
key: 'A',
url: 'productA/*',
label: <Link to='products/productA'>ProdcutA</Link>,
component: <ProdcutA />
},
{
key: 'B',
url: 'productB/*',
label: <Link to='products/productB'>ProdcutB</Link>,
component: <ProdcutB />
}
]
}
]

const Header: React.FC = () => {
return (
<>
{/* <Menu mode="horizontal">
{items.map((item: any) =>
item.children ? (
<Menu.SubMenu key={item.key} title={item.label}>
{item.children.map((subItem: any) => (
<Menu.Item key={subItem.key}>{subItem.label}</Menu.Item>
))}
</Menu.SubMenu>
) : (
<Menu.Item key={item.key}>{item.label}</Menu.Item>
)
)}
</Menu> */}
<Menu mode='horizontal' items={items} />
</>
)
}

export default Header
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
// RouteComponent.tsx
import { Routes, Route } from 'react-router-dom'
import Header from './components/Header'
import { items } from './components/Header'

function renderRoute(routes: any) {
return routes.map((route: any) =>
route.children ? (
route.children.map((subRoute: any) => (
<Route key={route.key} path={route.url} element={route.component}>
<Route key={subRoute.key} path={subRoute.url} element={subRoute.component} />
</Route>
))
) : (
<Route key={route.key} path={route.url} element={route.component} />
)
)
}

export default function App() {
return (
<>
<Header />
<Routes>{renderRoute(items)}</Routes>
</>
)
}

想看範例的話一樣到 這邊 來看。

初探 React Native 與環境建置 Ant Design-DatePicker
Your browser is out-of-date!

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

×