react+zarm實(shí)現(xiàn)底部導(dǎo)航欄的示例代碼
需要實(shí)現(xiàn)的效果
需要實(shí)現(xiàn)下面欄目固定,并且點(diǎn)擊時(shí)切換到不同頁面路由

實(shí)現(xiàn)過程
1.使用 prop-types 庫進(jìn)行類型檢查
注意:自 React v15.5 起,React.PropTypes 已移入另一個(gè)包中。請使用 prop-types 庫 代替。
PropTypes 提供了使用不同驗(yàn)證器的例子:
import PropTypes from 'prop-types';
MyComponent.propTypes = {
// 你可以將屬性聲明為 JS 原生類型,默認(rèn)情況下
// 這些屬性都是可選的。
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,
// 任何可被渲染的元素(包括數(shù)字、字符串、元素或數(shù)組)
// (或 Fragment) 也包含這些類型。
optionalNode: PropTypes.node,
// 一個(gè) React 元素。
optionalElement: PropTypes.element,
// 一個(gè) React 元素類型(即,MyComponent)。
optionalElementType: PropTypes.elementType,
// 你也可以聲明 prop 為類的實(shí)例,這里使用
// JS 的 instanceof 操作符。
optionalMessage: PropTypes.instanceOf(Message),
// 你可以讓你的 prop 只能是特定的值,指定它為
// 枚舉類型。
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
// 一個(gè)對象可以是幾種類型中的任意一個(gè)類型
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// 可以指定一個(gè)數(shù)組由某一類型的元素組成
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
// 可以指定一個(gè)對象由某一類型的值組成
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
// 可以指定一個(gè)對象由特定的類型值組成
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),
// An object with warnings on extra properties
optionalObjectWithStrictShape: PropTypes.exact({
name: PropTypes.string,
quantity: PropTypes.number
}),
// 你可以在任何 PropTypes 屬性后面加上 `isRequired` ,確保
// 這個(gè) prop 沒有被提供時(shí),會打印警告信息。
requiredFunc: PropTypes.func.isRequired,
// 任意類型的必需數(shù)據(jù)
requiredAny: PropTypes.any.isRequired,
// 你可以指定一個(gè)自定義驗(yàn)證器。它在驗(yàn)證失敗時(shí)應(yīng)返回一個(gè) Error 對象。
// 請不要使用 `console.warn` 或拋出異常,因?yàn)檫@在 `oneOfType` 中不會起作用。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
},
// 你也可以提供一個(gè)自定義的 `arrayOf` 或 `objectOf` 驗(yàn)證器。
// 它應(yīng)該在驗(yàn)證失敗時(shí)返回一個(gè) Error 對象。
// 驗(yàn)證器將驗(yàn)證數(shù)組或?qū)ο笾械拿總€(gè)值。驗(yàn)證器的前兩個(gè)參數(shù)
// 第一個(gè)是數(shù)組或?qū)ο蟊旧?
// 第二個(gè)是他們當(dāng)前的鍵。
customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
if (!/matchme/.test(propValue[key])) {
return new Error(
'Invalid prop `' + propFullName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
})
};
安裝依賴:
npm i prop-types -S

2.使用 useNavigate
v6 用 useNavigate 替代了 useHistory,其返回了一個(gè) navigate (點(diǎn)擊查看用法) 的方法,實(shí)現(xiàn)比較簡單:
- 從
NavigationContext拿到navigator,也就是history實(shí)例。 - 然后根據(jù)
to、matches的每項(xiàng)pathnameBase以及當(dāng)前URL pathname生成最終的路徑path({pathname, search, hash}) - 根據(jù)是否指定
replace來判斷是調(diào)用replace還是push方法
// v5
import { useHistory } from 'react-router-dom';
function MyButton() {
let history = useHistory();
function handleClick() {
history.push('/home');
};
return <button onClick={handleClick}>Submit</button>;
};
現(xiàn)在,history.push() 將替換為 navigation():
// v6
import { useNavigate } from 'react-router-dom';
function MyButton() {
let navigate = useNavigate();
function handleClick() {
navigate('/home');
};
return <button onClick={handleClick}>Submit</button>;
};
3.編寫標(biāo)簽欄組件
新建 components/NavBar/index.jsx 文件,用于編寫底部導(dǎo)航欄,代碼如下所示:
import React, { useState } from 'react';
import PropTypes from 'prop-types'
import { TabBar } from 'zarm';
import { useNavigate, useLocation } from 'react-router-dom';
import CustomIcon from '../CustomIcon'
import s from './style.module.less';
const NavBar = ({ showNav }) => {
const location = useLocation() // 拿到 location 實(shí)例
const { pathname } = location // 獲取當(dāng)前路徑
console.log('navbar pathname', pathname)
const [activeKey, setActiveKey] = useState(pathname);
const navigate = useNavigate()
const changeTab = (path) => {
setActiveKey(path)
navigate(path)
}
return (
<TabBar visible={showNav} className={s.tab} activeKey={activeKey} onChange={changeTab}>
<TabBar.Item
itemKey="/"
title="賬單"
icon={<CustomIcon type="zhangdan" />}
/>
<TabBar.Item
itemKey="/data"
title="統(tǒng)計(jì)"
icon={<CustomIcon type="tongji" />}
/>
<TabBar.Item
itemKey="/user"
title="我的"
icon={<CustomIcon type="user" />}
/>
</TabBar>
);
};
NavBar.propTypes = {
showNav: PropTypes.bool
}
export default NavBar;
新建 components/NavBar/style.module.less 文件,用于編寫底部導(dǎo)航欄樣式,代碼如下所示:
.tab {
border-top: 1px solid #e9e9e9;
}
4.使用標(biāo)簽欄組件
打開 App.jsx,添加如下代碼:
import React, { useState, useEffect } from 'react'
import NavBar from '@/components/NavBar';
import { Routes, Route, useLocation, BrowserRouter } from 'react-router-dom'
import { ConfigProvider } from 'zarm';
import routes from '../src/router'
function App() {
const location = useLocation() // 拿到 location 實(shí)例
const { pathname } = location // 獲取當(dāng)前路徑
const needNav = ['/', '/data', '/user'] // 需要底部導(dǎo)航欄的路徑
const [showNav, setShowNav] = useState(false) // 是否展示 Nav
useEffect(() => {
setShowNav(needNav.includes(pathname))
}, [pathname]) // [] 內(nèi)的參數(shù)若是變化,便會執(zhí)行上述回調(diào)函數(shù)
return <BrowserRouter>
<ConfigProvider primaryColor={'#007fff'}>
<Routes>
{
routes.map(route => <Route key={route.path} path={route.path} element={<route.component />}></Route>)
}
</Routes>
</ConfigProvider>
<NavBar showNav={showNav} />
</BrowserRouter>
}
export default App
我們發(fā)現(xiàn)報(bào)錯(cuò)了:Uncaught Error: You cannot render a <Router> inside another <Router>. You should never have more than one in your app.

這是因?yàn)橄胍诤瘮?shù)組件內(nèi)執(zhí)行 useLocation,該組件必須被 Router 高階組件包裹,我們做如下改動,將 App.jsx 的 BrowserRouter 組件,前移到 main.jsx 內(nèi),如下:
App.jsx 里面
import React, { useState, useEffect } from 'react'
import NavBar from '@/components/NavBar';
import { Routes, Route, useLocation } from 'react-router-dom'
import { ConfigProvider } from 'zarm';
import routes from '../src/router'
function App() {
const location = useLocation() // 拿到 location 實(shí)例
const { pathname } = location // 獲取當(dāng)前路徑
const needNav = ['/', '/data', '/user'] // 需要底部導(dǎo)航欄的路徑
const [showNav, setShowNav] = useState(false) // 是否展示 Nav
useEffect(() => {
setShowNav(needNav.includes(pathname))
}, [pathname]) // [] 內(nèi)的參數(shù)若是變化,便會執(zhí)行上述回調(diào)函數(shù)
return <>
<ConfigProvider primaryColor={'#007fff'}>
<Routes>
{
routes.map(route => <Route key={route.path} path={route.path} element={<route.component />}></Route>)
}
</Routes>
</ConfigProvider>
<NavBar showNav={showNav} />
</>
}
export default App
main.jsx :
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import 'lib-flexible/flexible'
import './index.css'
import App from './App'
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
)
5.添加對應(yīng)的頁面路由
在 container 文件夾里添加下面三個(gè)模塊的頁面

// 賬單
import React from 'react'
const Home = () => {
return <div>賬單</div>
}
export default Home
// 統(tǒng)計(jì)
import React from 'react'
const Data = () => {
return <div>統(tǒng)計(jì)</div>
}
export default Data
// 個(gè)人中心
import React from 'react'
const User = () => {
return <div>個(gè)人中心</div>
}
export default User
然后在 router/index.js 添加路由:
import Login from '@/container/Login'
import Home from '@/container/Home'
import Data from '@/container/Data'
import User from '@/container/User'
const routes = [
{
path: "/login",
component: Login
},{
path: "/",
component: Home
},{
path: "/data",
component: Data
},{
path: "/user",
component: User
}
];
export default routes
6.效果

我們可以切換到統(tǒng)計(jì),然后刷新,發(fā)現(xiàn)也是沒有問題。

參考資料
- React-Router v6 新特性解讀及遷移指南
- 系好安全帶,帶你遨游 React Router v6 源碼
- https://zarm.design/#/components/tab-bar
- 使用 PropTypes 進(jìn)行類型檢查
到此這篇關(guān)于react+zarm實(shí)現(xiàn)底部導(dǎo)航欄的示例代碼的文章就介紹到這了,更多相關(guān)react 底部導(dǎo)航欄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react-native滑動吸頂效果的實(shí)現(xiàn)過程
這篇文章主要給大家介紹了關(guān)于react-native滑動吸頂效果的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用react-native具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
react組件的創(chuàng)建與更新實(shí)現(xiàn)流程詳解
React組件分為函數(shù)組件與class組件;函數(shù)組件是無狀態(tài)組件,class稱為類組件;函數(shù)組件只有props,沒有自己的私有數(shù)據(jù)和生命周期函數(shù);class組件有自己私有數(shù)據(jù)(this.state)和生命周期函數(shù)2022-10-10
React?Hooks使用startTransition與useTransition教程示例
這篇文章主要為大家介紹了React?Hooks使用startTransition與useTransition教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
create-react-app中添加less支持的實(shí)現(xiàn)
這篇文章主要介紹了react.js create-react-app中添加less支持的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
React父子組件傳值(組件通信)的實(shí)現(xiàn)方法
本文主要介紹了React父子組件傳值(組件通信)的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
React?正確使用useCallback?useMemo的方式
這篇文章主要介紹了React?正確使用useCallback?useMemo的方式,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-08-08

