时间:2023-01-08 11:12:33 | 栏目: | 点击:次
需要实现下面栏目固定,并且点击时切换到不同页面路由
注意:自 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
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>; };
新建 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; }
打开 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') )
在 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
我们可以切换到统计,然后刷新,发现也是没有问题。