There's nothing here!
本文采用尚硅谷2021版React技术全家桶全套完整版中 p77- p93 视频中所使用案例进行演示。该视频中使用的为 V5 版本,所以 V5 版本的代码不会进行过多演示。主要演示V6版本中的替代/解决方案(非实际开发中最优方案,仅仅以视频中的案例为基础对V6版进行演示)。
正文
在React Router v6版本中大量使用了React hooks,因此在尝试使用v6版本之前,需要使用React 16.8或更高版本。——来自React Router AIP
由此可见。我们在接下来的代码中将会使用React hooks相关内容。当然您如果没有接触过相关知识也没有关系,这篇文章的重点并不是React hooks,就算没有了解过也没有太大问题。
OK 接下来我们从
路由的基本使用
开始(仅展示关键代码,完整代码请查看尚硅谷视频)
首先,在App.js中我们定义路由
{/* 注册路由 */}
在V6版本中,与V5版本中不一致。V5版本中注册路由,无需包裹
Routes与Route是基于当前位置渲染内容的主要方法,Route可以想象为if语句,如果URL匹配则呈现元素。
在V6的版本中,Route添加了caseSensitive属性则确定是否以区分大小写的方式进行匹配,该属性默认值为 false。
NavLink的基本使用
我们都知道NavLink在被选中时会被自动添加一个active的class。
在V5版本中,改变NavLink选中样式使用的是activeClassName属性进行替换掉NavLink的active为自定义class,而V6版本中,已将此属性移除。
尚硅谷视频截图NavLink基本使用P79
在V6版本中改变NavLink选中样式可以使用如下方法:
1.使用style属性:
如下在style属性中传递一个回调函数,其回调函数含有一个isActive状态来辨别是否被选中,该值为boolean值。通过该回调函数来指定行内样式(优先级较高)NavLink还是会为我们自动添加 active 样式类。
return isActive ? {
zIndex: 2,
color: "#fff",
backgroundColor: "rgb(247 151 60)",
borderColor: "rgb(247 151 60)"
} : null
}} className="list-group-item" to="/about">About
2.直接修改className
to="/about"
>Home
同样是通过回调函数中的 isActive 进行判断,然后返回相应样式类名(其中diyActive是我自己定义的一个样式,此处不进行过多展示)。这种方案NavLink则不会自动添加 active 样式类。
Switch组件
在V6版本中Switch组件已移除。在上面文字中也说了使用Routes进行包裹,且是必须的。使用V6版本的Routes进行包裹。则不会出现继续向下匹配组件的问题。且没有V5版本中继续向下匹配路由问题。以及P83中的模糊匹配与严格匹配,在V6版本中exact属性已被移除,所有的路由都是“严格匹配”。
Redirect组件——>Navigate组件
在V6版本中Redirect组件已被移除。在V5版本中使用
通过Route组件中的path="*"来对未匹配的链接进行“兜底”,通过Navigate来实现跳转到哪里去。
当然如果你并不想跳转也可以使用下列代码(来自官方API)来自定义未匹配的内容
There's nothing here!
element={
}
/>
上面这段代码的意思是,未匹配的内容会在这里输出There's nothing here!这段文字。当然你也可以改成自己喜欢的形状。。
关于Navigate组件:
只要在组件内写了写了Navigate则会一直调用。。除了上面的这种应用方式。感觉其它地方不太好用。如果想要实现点击按钮跳转链接或者定时跳转请使用useNavigate。useNavigate在下面内容中会演示使用方法,请继续往下看。Navigate组件与Link、NavLink的属性差不多。都含有to、state、replace属性。使用起来没什么难度。
嵌套路由
在V5版本的二级路由的注册是直接写在想要展示的位置。而在V6版本中则并不是这样。
尚硅谷视频截图P85嵌套路由(Home路由组件)
在V6版本中我们这样写:
Home组件:
return (
OK。在Home组件中我们可以看到,在里面并没有像截图的红框中直接注册路由。而是写了一个
这里需要注意的一点是 在注册路由中,父路由组件的path需要写为"/home/*"末尾一定要添加/*,否则无法实现子组件默认路由选中。
TIP:
MyNavLink是自定义的NavLink组件。代码如下:
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
export default class MyNavLink extends Component {
render() {
return (
{...this.props}
/>
)
}
}
向路由组件传递params参数
好的,首先我们来定义Massage组件,在Message组件中向Detail组件转递params参数
import React, { Component } from 'react'
import { Link, Outlet } from 'react-router-dom'
export default class Message extends Component {
state = {
messageArr: [
{ id: '01', title: 'message1' },
{ id: '02', title: 'message2' },
{ id: '03', title: 'message3' }
]
}
render() {
return (
{/* 传递参数 */}
注册路由部分在path中使用 /:param
前面这些都没有太大变化,重点部分为Message的子组件Detail。
首先我们来看V5版本的处理方式:
尚硅谷视频P86传递Params参数Detail组件
好的接下来看在V6版本中如何处理:
import React, { Component } from 'react';
import { useParams } from 'react-router-dom';
// v6使用class组件。需要封装一下。利用hoc组件来获取参数,然后传递给class组件
function myWithRouter(Detail) {
return (props) => {
return
}
}
const DetailData = [
{ id: '01', content: '11111111111111111111' },
{ id: '02', content: '22222222222222222222' },
{ id: '03', content: '33333333333333333333' }
]
class Detail extends Component {
render() {
const contentStr = DetailData.find((detailObj) => {
return detailObj.id === this.props.params.id;
})
return (
export default myWithRouter(Detail);
这里我们可以看到,上面的Detail组件并没有直接export default进行暴露,而是暴露了myWithRouter(Detail)。这是为什么呢?
原因就是在V6版本中,不能V5版本中那样从this.props中获取路由组件的相关参数了。你如果打印一下props就会发现,props中毛都没有。这里V6版本中就提供了一个hook(钩子),来让我们可以直接获取到所传递的params参数,这个钩子就是 useParams。那么我们为毛不直接使用呢?因为React的hook只能在hoc函数高阶组件中使用。看到这里是不是就明白了为什么要包一层了吧。
上面代码中定义了一个myWithRouter函数组件并将Detail组件传入,在myWithRouter中获取useParams以及相关props,直接传递给Detail组件使用。这样我们就可以在Detail组件中成功的获取到params参数了。
当然上面不是最好的写法。我们直接用函数组件不是更好么。都不需要再进行在外面封装一层了。当然这里对于高阶组件不进行过多探讨,小伙伴们请自己去学习吧。高阶组件目前是React官方推荐的编码方式哦。
向路由组件传递search参数
传递search参数的Message组件与注册路由方式与传递params参数多数代码相似,这里就不过多编写,只展示不同部分
Message组件:
this.state.messageArr.map((messageObj) => {
return (
App.js正常注册路由:
{/* 传递参数 */}
Detail接收参数:
与传递params参数相同,还是需要使用高阶函数组件进行封装。这里V6版本中提供了useSearchParams 钩子。代码如下:
import React, { Component } from 'react';
import { useSearchParams } from 'react-router-dom';
// v6使用class组件。需要封装一下。利用hoc组件来获取参数,然后传递给class组件
function myWithRouter(Detail) {
return (props) => {
// let [searchParams, setSearchParams] = useSearchParams();
let [searchParams] = useSearchParams();
const params = {
id: searchParams.get('id'),
title: searchParams.get('titile')
}
return
}
}
const DetailData = [
{ id: '01', content: '11111111111111111111' },
{ id: '02', content: '22222222222222222222' },
{ id: '03', content: '33333333333333333333' }
]
class Detail extends Component {
render() {
const contentStr = DetailData.find((detailObj) => {
return detailObj.id === this.props.params.id;
})
return (
export default myWithRouter(Detail);
可以看到上面代码中被注释掉的代码let [searchParams, setSearchParams] = useSearchParams();可见useSearchParams不仅提供了获取searchParams参数,还提供了设置searchParams参数。可以通过setSearchParams进行设置。这里就不为大家过多演示。请大家自己尝试吧。
向路由组件传递state参数
传递state参数在Message组件中与V5版本稍微有一点区别,不过区别不是太大。
先看V5版本:
尚硅谷视频P88传递state参数
在V6版本中经过我测试,在to中写对象传state的方式并没有用。V6版本中在Link组件(包含NavLink、Navigate)中state作为一个单独的属性与to分开。
请看下列Message组件关键代码:
this.state.messageArr.map((messageObj) => {
return (
import React, { Component } from 'react';
import { useLocation } from 'react-router-dom';
// v6使用class组件。需要封装一下。利用hoc组件来获取参数,然后传递给class组件
function myWithRouter(Detail) {
return (props) => {
let location = useLocation();
/*
这里写成 location.state || {}
因为state在刷新后可能为空,所以这里为空时设置为空对象
*/
const stateObj = location.state || {};
const params = {
id: stateObj.id,
title: stateObj.title
}
return
}
}
const DetailData = [
{ id: '01', content: '11111111111111111111' },
{ id: '02', content: '22222222222222222222' },
{ id: '03', content: '33333333333333333333' }
]
class Detail extends Component {
render() {
const contentStr = DetailData.find((detailObj) => {
return detailObj.id === this.props.params.id;
}) || {}; // 这里 || {} 当未找到匹配的值时contentStr设置为空对象
return (
export default myWithRouter(Detail);
编程式路由导航
上面已经提到过路由组件中的props已经和普通组件一样不会默认传递路由相关参数了。所以还是需要使用高阶组件来实现。
首先还是来看一下V5版本中Message组件处理方式:
尚硅谷视频P91编程式路由导航
从上面图片可以看到实现前进、后退、前进两次,都是使用的this.props.history的方式。
而在V6版本中则提供了useNavigate来实现前进、后退等操作。下面就来看下Message的代码吧。
import React, { Component } from 'react'
import { Link, Outlet, useNavigate } from 'react-router-dom'
function anonyCom(MessCom) {
return (props) => {
let navigate = useNavigate();
return
}
}
class Message extends Component {
state = {
messageArr: [
{ id: '01', title: 'message1' },
{ id: '02', title: 'message2' },
{ id: '03', title: 'message3' }
]
}
pushShow = (messageObj) => {
this.props.navigate(
'/home/message/detail',
{
state: {
id: messageObj.id, title: messageObj.title
}
})
}
replaceShow = (messageObj) => {
this.props.navigate('/home/message/detail',
{
replace: true,
state: {
id: messageObj.id, title: messageObj.title
}
})
}
goBack = () => {
this.props.navigate(-1);
}
goOne = () => {
this.props.navigate(1);
}
goTwo = () => {
this.props.navigate(2);
}
render() {
return (
export default anonyCom(Message);
从上面代码中可以看出useNavigate不仅可以传递路由地址,还可以传递数字,传递正数则向前进,传递负数则向后退。并且还可以传递一个对象,对象中包含了replace与state。非常的强大。
到这里我们可以看到在新版本中,大部分功能都改为了Hook的方式使用。所以如果需要使用新版本则一定要学会Hook相关知识。
其它
在V5版本中还有withRouter相关内容。在V6版本中withRouter已经移除。所以在这里就不过多讲解了。若需要实现在头部组件(一般组件)中如何使用路由组件的相关案例。其实我们上面使用高阶函数封装就是一直在写一个自定义的withRouter。。