react+zarm实现底部导航栏的示例代码
作者:凯小默:树上的男爵
需要实现的效果
需要实现下面栏目固定,并且点击时切换到不同页面路由
实现过程
1.使用 prop-types 库进行类型检查
注意:自 React v15.5 起,React.PropTypes 已移入另一个包中。请使用 prop-types 库 代替。
PropTypes 提供了使用不同验证器的例子:
import PropTypes from 'prop-types'; MyComponent.propTypes = { // 你可以将属性声明为 JS 原生类型,默认情况下 // 这些属性都是可选的。 optionalArray: PropTypes.array, optionalBool: PropTypes.bool, optionalFunc: PropTypes.func, optionalNumber: PropTypes.number, optionalObject: PropTypes.object, optionalString: PropTypes.string, optionalSymbol: PropTypes.symbol, // 任何可被渲染的元素(包括数字、字符串、元素或数组) // (或 Fragment) 也包含这些类型。 optionalNode: PropTypes.node, // 一个 React 元素。 optionalElement: PropTypes.element, // 一个 React 元素类型(即,MyComponent)。 optionalElementType: PropTypes.elementType, // 你也可以声明 prop 为类的实例,这里使用 // JS 的 instanceof 操作符。 optionalMessage: PropTypes.instanceOf(Message), // 你可以让你的 prop 只能是特定的值,指定它为 // 枚举类型。 optionalEnum: PropTypes.oneOf(['News', 'Photos']), // 一个对象可以是几种类型中的任意一个类型 optionalUnion: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, PropTypes.instanceOf(Message) ]), // 可以指定一个数组由某一类型的元素组成 optionalArrayOf: PropTypes.arrayOf(PropTypes.number), // 可以指定一个对象由某一类型的值组成 optionalObjectOf: PropTypes.objectOf(PropTypes.number), // 可以指定一个对象由特定的类型值组成 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` ,确保 // 这个 prop 没有被提供时,会打印警告信息。 requiredFunc: PropTypes.func.isRequired, // 任意类型的必需数据 requiredAny: PropTypes.any.isRequired, // 你可以指定一个自定义验证器。它在验证失败时应返回一个 Error 对象。 // 请不要使用 `console.warn` 或抛出异常,因为这在 `oneOfType` 中不会起作用。 customProp: function(props, propName, componentName) { if (!/matchme/.test(props[propName])) { return new Error( 'Invalid prop `' + propName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } }, // 你也可以提供一个自定义的 `arrayOf` 或 `objectOf` 验证器。 // 它应该在验证失败时返回一个 Error 对象。 // 验证器将验证数组或对象中的每个值。验证器的前两个参数 // 第一个是数组或对象本身 // 第二个是他们当前的键。 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
,其返回了一个 navigate
(点击查看用法) 的方法,实现比较简单:
- 从
NavigationContext
拿到navigator
,也就是history
实例。 - 然后根据
to、matches
的每项pathnameBase
以及当前URL pathname
生成最终的路径path({pathname, search, hash})
- 根据是否指定
replace
来判断是调用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>; };
现在,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.编写标签栏组件
新建 components/NavBar/index.jsx
文件,用于编写底部导航栏,代码如下所示:
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 实例 const { pathname } = location // 获取当前路径 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="统计" 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
文件,用于编写底部导航栏样式,代码如下所示:
.tab { border-top: 1px solid #e9e9e9; }
4.使用标签栏组件
打开 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 实例 const { pathname } = location // 获取当前路径 const needNav = ['/', '/data', '/user'] // 需要底部导航栏的路径 const [showNav, setShowNav] = useState(false) // 是否展示 Nav useEffect(() => { setShowNav(needNav.includes(pathname)) }, [pathname]) // [] 内的参数若是变化,便会执行上述回调函数 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
我们发现报错了:Uncaught Error: You cannot render a <Router> inside another <Router>. You should never have more than one in your app.
这是因为想要在函数组件内执行 useLocation,该组件必须被 Router 高阶组件包裹,我们做如下改动,将 App.jsx
的 BrowserRouter 组件,前移到 main.jsx
内,如下:
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 实例 const { pathname } = location // 获取当前路径 const needNav = ['/', '/data', '/user'] // 需要底部导航栏的路径 const [showNav, setShowNav] = useState(false) // 是否展示 Nav useEffect(() => { setShowNav(needNav.includes(pathname)) }, [pathname]) // [] 内的参数若是变化,便会执行上述回调函数 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.添加对应的页面路由
在 container 文件夹里添加下面三个模块的页面
// 账单 import React from 'react' const Home = () => { return <div>账单</div> } export default Home // 统计 import React from 'react' const Data = () => { return <div>统计</div> } export default Data // 个人中心 import React from 'react' const User = () => { return <div>个人中心</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.效果
我们可以切换到统计,然后刷新,发现也是没有问题。
参考资料
- React-Router v6 新特性解读及迁移指南
- 系好安全带,带你遨游 React Router v6 源码
- https://zarm.design/#/components/tab-bar
- 使用 PropTypes 进行类型检查
到此这篇关于react+zarm实现底部导航栏的示例代码的文章就介绍到这了,更多相关react 底部导航栏内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!