因为是主学后端,为了做项目学点前端。下面的代码都是个人练习,主要是实现导航菜单进行路由跳转,分享一下学习收获和解决一些bug的所得。不足之处还请大佬多指点。
附注:本文章多是参考其他文章的代码案例进行实现,主要自己的东西还是解决bug的心得。
参考文章:
目录
提示:需要学习导航菜单实现路由跳转的功能的请参考上面文章,本文有价值的不过只是排错和收获。欢迎指正!
代码如下
1. 依赖
"antd": "5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.4.5",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
2. 包结构
红色方块是主要的组件。
用的是编程式跳转的方式
3. 代码实现
配置主页和组件
App.js
import {
Route,
Navigate,
Routes
} from 'react-router-dom'
import Main from './Main';
import CardTest from './pages/CardTest';
import LayOutPage from './pages/LayOutPage';
import TestPage1 from './pages/testPage1';
export default function App() {
return (
//这里用Routes,后面再来解释原因。
<Routes>
<Route path='*' element={<Navigate to="/home" />} />
<Route exact path='/home' element={<Main />}>
<Route exact path='/home/first' element={<CardTest />} />
<Route exact path='/home/second' element={<LayOutPage />} />
<Route exact path='/home/third' element={<TestPage1 />} />
</Route>
</Routes>
)
}
主页组件
Main.js
import React, { Component } from 'react';
import { Breadcrumb, Layout } from 'antd';
import { Outlet } from 'react-router-dom'
import './index.css'; //这里是简单将部分样式转移到css,是个人尝试以及练习css,不必在意
import NavigateComponent from './components/headMenu';
const { Header, Content, Footer } = Layout;
class Main extends Component {
render() {
return (
<Layout>
<Header
style={{
position: 'sticky',
top: 0,
zIndex: 1,
width: '100%',
}}
>
<div
style={{
float: 'left',
width: 120,
height: 31,
margin: '16px 24px 16px 0',
background: 'rgba(255, 255, 255, 0.2)',
}}
/>
{/* 渲染导航菜单组件 */}
<NavigateComponent />
</Header>
<Content
className="site-layout"
style={{
padding: '0 50px',
}}
>
<Breadcrumb
style={{
margin: '16px 0',
}}
>
<Breadcrumb.Item>Home</Breadcrumb.Item>
<Breadcrumb.Item>List</Breadcrumb.Item>
<Breadcrumb.Item>App</Breadcrumb.Item>
</Breadcrumb>
<div id='context'
>
内容组件
{/* 路由跳转 */}
<Outlet />
</div>
</Content>
<Footer
style={{
textAlign: 'center',
}}
>
To be a better man
</Footer>
</Layout>
)
}
};
export default Main;
路由跳转的组件,其他的都是简单的展示,主要目的只是展示效果。下面只展示一个组件,其他的组件名由上面App.js可见,我放在page包下
LayOutPage.js
import {Component} from "react";
export default class LayOutPage extends Component {
render(){
return (
<div>看看我!</div>
)
}
}
导航菜单组件
/components/headMenu.js
import { Component } from "react";
import { Menu } from "antd";
import widthUseNavigate from "../utils/getUseNavigate";
class HeaderMenu extends Component {
constructor(props) {
super(props);
this.state = {
items: [
{
key: '/home/first',
label: `组件1`
},
{
key: '/home/second',
label: `组件2`
},
{
key: '/home/third',
label: `组件3`
}
]
}
}
click = (e) => {
this.props.to(e.key);
}
render() {
return (
/* 导航菜单组件 */
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={['/first']}
items={this.state.items}
onClick={this.click}
/>
)
}
}
// 使用高阶组件包裹当前类组件
const NavigateComponent = widthUseNavigate(HeaderMenu);
export default NavigateComponent;
注意click函数,this.prop.to的实现需要下面高阶组件的配合实现。
高阶组件,包裹useNavigate()功能
widthUseNavigate.js
注:这一段代码真正是cv大佬文章的,我自己水平是写不出的哈哈。
import { useNavigate } from 'react-router-dom'
// 高阶组件包装useNavigate()功能
// 原因:类组件中无法使用useNavigate(),会报错
// error: React Hook "useNavigate" cannot be called in a class component.
function widthUseNavigate(WrapCompontent) {
// 设置别名
WrapCompontent.displayName = `widthUseNavigate${getDisplayName(WrapCompontent)}`
return function NavigateCompont() {
const navigate = useNavigate()
// 给传入的组件新增一个to方法,传给原始组件的props,在原始组件中通过this.props.to(参数)使用
return <WrapCompontent to={navigate}></WrapCompontent>
}
}
// 别名
function getDisplayName(WrapCompontent) {
return WrapCompontent.displayname || WrapCompontent.name || 'Component'
}
export default widthUseNavigate
最后是渲染到页面
index.js
import ReactDOM from 'react-dom/client';
// import './index.css';
import App from './App';
import {BrowserRouter as Router} from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Router>
<App/>
</Router>
);
这里<App/>包裹了<Router>,也是我在排错时参考大佬文章做出的调整。下面再提。
到这里,代码实现完成。项目启动后功能正常实现。
排错
中间许多排错过程真可谓是悠悠万难哈哈。没做记录,下面来说一下做出了那些排错调整,以及原因。
第一点:
在一整个模仿加cv简单的实现导航菜单和主页面功能。开始尝试实现路由跳转的时候,被<Router>标签包裹的页面无法显示,翻遍百度之后尝试了一种做法就是将要作为渲染到页面的App组件用<Router>标签包裹,而App组件中管理的子路由组件用<Routes>包裹。
index.js
import ReactDOM from 'react-dom/client';
// import './index.css';
import App from './App';
import {BrowserRouter as Router} from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Router>
<App/>
</Router>
);
App.js
import {
Route,
Navigate,
Routes
} from 'react-router-dom'
import Main from './Main';
import CardTest from './pages/CardTest';
import LayOutPage from './pages/LayOutPage';
import TestPage1 from './pages/testPage1';
export default function App() {
return (
<Routes>
<Route path='*' element={<Navigate to="/home" />} />
<Route exact path='/home' element={<Main />}>
<Route exact path='/home/first' element={<CardTest />} />
<Route exact path='/home/second' element={<LayOutPage />} />
<Route exact path='/home/third' element={<TestPage1 />} />
</Route>
</Routes>
)
}
原因是:
而<Router>标签不能够双层包裹,而App组件中管理路由需要用<Router>标签包裹。所以换用<Routes>标签解决。
第二点:
在导航菜单中实现路由跳转控制,而useNavigate功能无法在类组件中实现,会报错:
error: React Hook "useNavigate" cannot be called in a class component.
所以用高阶组件包裹useNavigate方法,再暴露给导航菜单组件调用
getUseNavigate.js
import { useNavigate } from 'react-router-dom'
// 高阶组件包装useNavigate()功能
// 原因:类组件中无法使用useNavigate(),会报错
// error: React Hook "useNavigate" cannot be called in a class component.
function widthUseNavigate(WrapCompontent) {
// 设置别名
WrapCompontent.displayName = `widthUseNavigate${getDisplayName(WrapCompontent)}`
return function NavigateCompont() {
const navigate = useNavigate()
// 给传入的组件新增一个to方法,传给原始组件的props,在原始组件中通过this.props.to(参数)使用
return <WrapCompontent to={navigate}></WrapCompontent>
}
}
// 别名
function getDisplayName(WrapCompontent) {
return WrapCompontent.displayname || WrapCompontent.name || 'Component'
}
export default widthUseNavigate
在导航菜单调用,并将返回的增强后的组件暴露
以上是文章的全部,再次感谢各位大佬的分享和帮助。
Cannot read properties of undefined (reading 'pathname') | bobbyhadz