目录
  • 需要实现的效果
  • 实现过程
    • 1.使用 prop-types 库进行类型检查
    • 2.使用 useNavigate
    • 3.编写标签栏组件
    • 4.使用标签栏组件
    • 5.添加对应的页面路由
    • 6.效果
  • 参考资料

    需要实现的效果

    需要实现下面栏目固定,并且点击时切换到不同页面路由

    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

    react+zarm实现底部导航栏的示例代码

    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.

    react+zarm实现底部导航栏的示例代码

    这是因为想要在函数组件内执行 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 文件夹里添加下面三个模块的页面

    react+zarm实现底部导航栏的示例代码

    // 账单
    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+zarm实现底部导航栏的示例代码

    我们可以切换到统计,然后刷新,发现也是没有问题。

    react+zarm实现底部导航栏的示例代码

    参考资料

    • React-Router v6 新特性解读及迁移指南
    • 系好安全带,带你遨游 React Router v6 源码
    • https://zarm.design/#/components/tab-bar
    • 使用 PropTypes 进行类型检查

     到此这篇关于react+zarm实现底部导航栏的示例代码的文章就介绍到这了,更多相关react 底部导航栏内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。