react补充点
邵预鸿 Lv5

react 补充

react父传子组件

组件通过{…info}将整个对象结构传给子组件

1
2
3
4
5
6
7
const info = {
name:'shaoyuhong',
age:22,
school:'重庆师范大学'
}
<About {...info}></About>
<About name={info.name} age={info.age} school={info.school}></About>

设置props的类型约束

1
2
3
4
5
6
About.propTypes = {
age:PropTypes.number.isRequired
}
About.defaultProps = {
money:'22K'
}

将类型约束挂载到实例类上是一种方式,另外一种方式是将约束放到类的静态方式中,参考下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class About extends Component{
static propTypes = {
age:PropTypes.number.isRequired
}
static defaultProps = {
money:'22K'
}
constructor(props){
super(props);
console.log(props);
}
render(){
return (<div className='about'>
<hr/>
<h2>this is about.js</h2>
<h3>接收的参数:{JSON.stringify(this.props)}</h3>
</div>)
}
}

参考react网址:https://react.docschina.org/docs/typechecking-with-proptypes.html

ref的三种方式

  • 字符串的形式 该方式不建议使用,会在将来版本中或废弃

    <button className='btn' ref="submitBtn" onClick={this.log}>提交</button>

    取当前dom时

    this.refs.submitBtn

  • 回调函数的方式

    <button className='btn' ref={dom=>this.btn = dom} onClick={this.log}>提交</button>

    取当前dom时

    this.btn

  • 使用react.createRef() 创建ref

    import {createRef} from ‘react’;

    constructor(props){

    ​ super(props);

    ​ this.submit = createRef();

    }

    <button className='btn' ref={this.submit } onClick={this.log}>提交</button>

    取当前dom时

    this.submit.current

设置对象时,当属性名是变量时

1
2
3
4
const keys = 'name';
this.setState({
[keys]:'xxx'
})

高阶函数定义

传入的参数是一个函数 or 函数内部的表达式返回了一个函数

函数柯里化

通过函数调用返回函数继续处理的方式,实现多次传入参数最终统一处理

1
2
3
4
5
6
7
8
function a(num1){
return function(num2){
return function(num3){
return num1+num2+num3;
}
}
}
console.log(a(1)(2)(3));

SPA

spa:single page application 单页面应用

  • 整个页面只有一个完整的html页面
  • 页面切换不会刷新,通过H5中的pushState和replaceState实现对URL修改
  • 数据通过ajax获取

this.setState

  • this.setState({[变量] : 值 })

  • this.setState((prevState,props)=>{

    return {

    ​ count:prevState.count + 1

    }

    });

路由懒加载

打包是个非常棒的技术,但随着你的应用增长,你的代码包也将随之增长。尤其是在整合了体积巨大的第三方库的情况下。你需要关注你代码包中所包含的代码,以避免因体积过大而导致加载时间过长。

为了避免搞出大体积的代码包,在前期就思考该问题并对代码包进行分割是个不错的选择。 代码分割是由诸如 WebpackRollup 和 Browserify(factor-bundle)这类打包器支持的一项技术,能够创建多个包并在运行时动态加载。

对你的应用进行代码分割能够帮助你“懒加载”当前用户所需要的内容,能够显著地提高你的应用性能。尽管并没有减少应用整体的代码体积,但你可以避免加载用户永远不需要的代码,并在初始加载的时候减少所需加载的代码量。

React.lazy

React.lazy 函数能让你像渲染常规组件一样处理动态引入(的组件)。

使用之前:

1
import OtherComponent from './OtherComponent';

使用之后:

1
const OtherComponent = React.lazy(() => import('./OtherComponent'));

一定是配合 <Suspense fallback={<div>Loading...</div>}> {element} </Suspense>使用,在网页加载太慢时,将优先展示fallback中的组件

在v6路由中如何 使用?

router.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import { BrowserRouter, HashRouter, Routes, Route,Navigate } from 'react-router-dom';
const Error = lazy(()=>import('@/views/404'));
const About = lazy(()=>import('@/views/about'));
const Message = lazy(()=>import('@/views/message'));
const AboutView1 = lazy(()=>import('@/views/about_child1'));
const AboutView2 = lazy(()=>import('@/views/about_child2'));
const AboutView3 = lazy(()=>import('@/views/about_child3'));
const CanvasTest = lazy(()=>import('@/views/canvas'));


const routers = [
{
path:'message',
element:<Message/>
},
{
path:'ctx',
element:<CanvasTest/>
},
{
path:'about/*',
element:<About/>,
children:[
{
path:'tab1',
element:<AboutView1/>
},
{
path:'tab2',
element:<AboutView2/>
},
{
path:'tab3',
element:<AboutView3/>
},
{
path:'*',
element:<Navigate to="tab1"></Navigate>
}
]
},
{
path:'404',
element:<Error/>
},
{
path:'*',
element:<Navigate to="message"></Navigate>
}
]
export default routers;

app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React, { Suspense } from 'react';
function App() {
const element = useRoutes(RoutersList); //使用路由配置表

return (
<div className="app">
<main>
<nav></nav>
<section>
<Suspense fallback={<div>Loading...</div>}>
{element}
</Suspense>
</section>
</main>
</div>
);
}

参考react官方:https://zh-hans.reactjs.org/docs/code-splitting.html

WithRouter

当某个组件没有在路由配置中,如<Footer> <Header>类型组件,在公共的layout中,这类组件并不在路由配置中,此时这两个组件没有this.props.history,使用withRouter(Footer)可以拥有路由实例方法和属性

针对V5 react-react-dom,v6可以直接使用useNavigate()

针对react class类式组件的父孙间传值

  • 需要一个全局公共变量、可以是模块化导出的变量const ThemeContext = React.createContext(‘’);

  • 父组件使用<ThemeContext.provider value={值}></ThemeContext.provider>向下暴露值

    1
    2
    3
    <moduleC.Provider value={fullName}>
    <D></D>
    </moduleC.Provider>
  • 子组件接收 只针对类式组件 static contextType = ThemeContext 结果将在this.context中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import E from './e';
    import { moduleC } from '@/comm';
    import React from 'react';
    class D extends React.Component {
    static contextType = moduleC;
    constructor(props) {
    super(props);
    }
    render() {
    console.log(this.context);
    return (<div className="d">
    <h2>这是D组件</h2>
    <h3>接收到的值:{this.context}</h3>
    <E></E>
    </div>)
    }

    }
    export default D;

函数式组件中的每一次update更新,都将触发整个函数执行

useState中的值第一次执行后会被暂存,update更新时,将使用上一次暂存的值,而不是再重新执行useState(默认值)中的值

puleComponent

React.PureComponentReact.Component 很相似。两者的区别在于 React.Component 并未实现 shouldComponentUpdate(),而 React.PureComponent 中以浅层对比 prop 和 state 的方式来实现了该函数。

如果赋予 React 组件相同的 props 和 state,render() 函数会渲染相同的内容,那么在某些情况下使用 React.PureComponent 可提高性能。

shouldComponentUpdate

shouldComponentUpdate(nextProps,nextState)

  • 返回true,子组件更新

  • 返回false,子组件不更新

    通过对比当前this.props.xxx的值与下一次nextProps.xxx的值,true更新,false不更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--Parent-->
<Child math={this.state.math}></Child>

<!-- Child -->
import React, { Component,PureComponent } from 'react'
class Child extends Component{
constructor(props){
super(props);
}
shouldComponentUpdate(nextProps, nextState){
return nextProps.math !== this.props.math;
}
render(){
console.log('我是child.js render方法');
return (<section className="child">
<h1>Child.js</h1>
<h3>{this.props.math}</h3>
</section>);
}
}
export default Child;
如果是更为复杂的对象时怎么办?

考虑使用puleComponent

react中的插曹高级玩法

1
2
3
4
5
6
<!--App.jsx-->
<div>
<Child math={this.state.math}>
<Sun></Sun>
</Child>
</div>

有一个需求,我希望不动结构的情况下,sun传入一个chlid组件中的state数据

如何能解决这个问题?

1
2
3
4
5
<!--App.jsx-->
<div>
<Child math={this.state.math} renderFn = {(slotName)=><Sun slotName={slotName}></Sun>}>
</Child>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--Child.jsx-->
import React, { Component,PureComponent } from 'react'
class Child extends Component{
constructor(props){
super(props);
}
state = {
slotName:'我是chlid.js中的slot数据'
}
render(){

return (<section className="child">
<h1>Child.js</h1>
<h3>{this.props.math}</h3>
{this.props.renderFn(this.state.slotName)}
</section>);
}
}
export default Child;

在App.jsx中传入一个自定义函数,函数返回Child组件

**这样,Child.jsx中通过this.props.renderFn()调用就可以把标签放到指定的位置 **

this.props.renderFn(child.jsx中的state)传入后,App.jsx自定义函数接收参数,并往Sun组件传入

OK啦

高阶组件问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 此函数接收一个组件...
function withSubscription(WrappedComponent, selectData) {
// ...并返回另一个组件...
return class extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: selectData(DataSource, props)
};
}

componentDidMount() {
// ...负责订阅相关的操作...
DataSource.addChangeListener(this.handleChange);
}

componentWillUnmount() {
DataSource.removeChangeListener(this.handleChange);
}

handleChange() {
this.setState({
data: selectData(DataSource, this.props)
});
}

render() {
// ... 并使用新数据渲染被包装的组件!
// 请注意,我们可能还会传递其他属性
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}

react官方文档 高阶组件:https://zh-hans.reactjs.org/docs/higher-order-components.html

与vue中的mixins非常相同,可以集成公共方法,数据

react将组件挂载到其它元素上

支持将组件挂载到其它元素上,如常见的modal挂载到body,需要注意的是,虽然modal挂载到了body,但是事件处理时,依然会执行冒泡到代码父级的层级去

1
2
3
4
5
6
7
8
9
10
11
12
import { createPortal } from 'react-dom';
function Dialog(props) {
return createPortal(<div className="dialog">
<div className="box">
<div className="title">标题<span className="close">X</span></div>
<div className="wrap">
{props.children}
</div>
</div>
</div>, document.body)
}
export default Dialog;
例子 模拟antd-modal实现过程
1
2
3
4
5
6
7
8
9
10
/**
visible : 显示与隐藏
delDom : 隐藏时,是否删除dom元素
**/
<Dialog onClose={hiddenDialog} visible={show} delDom={true}>
<div>
1 入口文件 index.js
</div>
</Dialog>

如何实现内部配置delDom = false 时,删除子组件的dom元素, 很简单,多封装一层就可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { createPortal } from 'react-dom';
//dialog弹窗
function Dialog(props) {
const closeDialog = (e) => {
console.log(e);
e.stopPropagation();
props.onClose();
}
return createPortal(<div className={["dialog",props.visible?'' : 'none'].join(' ')}>
<div className="box">
<div className="title">标题<span className="close" onClick={closeDialog}>X</span></div>
<div className="wrap">
{props.children}
</div>
</div>
</div>, document.body)
}
//多包一层,用于控制是否清空dom
function DialogA(props) {
let component1 = '';
if(props.delDom){
if(!props.visible){
component1 = ''
}else{
component1 = <Dialog {...props}></Dialog>;
}
}else{
component1 = <Dialog {...props}></Dialog>;
}
return (<>
{component1}
</>
)
}
export default DialogA;

forwardRef父组件读取子组件的问题

App.js

  • 当子组件内部是使用class类式组件时,标签上的ref指向子组件内部的变量、方式、ref等,可以直接读取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    context: {}
    input: input
    props: {}
    refs: {}
    state: {name: 'yuhong', age: 22, count: 11}
    updater: {isMounted: ƒ, enqueueSetState: ƒ, enqueueReplaceState: ƒ, enqueueForceUpdate: ƒ}
    _reactInternalInstance: {_processChildContext: ƒ}
    _reactInternals: FiberNode {tag: 1, key: null, stateNode: InputGroup, elementType: ƒ, type: ƒ, …}
    isMounted: (...)
    replaceState: (...)
    [[Prototype]]: Component

  • 当子组件是函数式Function组件时,标签上的ref指向 的undefind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function App() {
const inputGroup = useRef();
const btnClick = ()=>{
console.log(inputGroup.current.input.value = '我是父组件强制写入的值');
}
return (
<div className="App">
<nav></nav>
<main onClick={() => { console.log('main is clik') }}>
<button onClick={btnClick}>input获取焦点</button>
<InputGroup ref={inputGroup}></InputGroup>
<List></List>
</main>
</div>
);
}

如果父组件需要操作子组件元素,由于父组件无法获取使用function定义的子组件实例,可以采用

1
2
3
4
5
6
7
const FancyButton = React.forwardRef((props, ref) => (  <button ref={ref} className="FancyButton">    {props.children}
</button>
));

// 你可以直接获取 DOM button 的 ref:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;

InputGroup.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class InputGroup extends React.Component{
constructor(props){
super(props);
}
state = {
name:'yuhong',
age:22,
count:11
}
render(){
return (<div>
<input type="text" ref={el=>this.input = el}/><button>提交</button>
</div>)
}
}

React.memo 与PuleComponent

类型组件使用PuleComponent来对比props减少子组件的更新

函数式组件使用React.memo来对比props减少子组件的更新

1
2
3
4
5
6
7
8
import {Component,PureComponent,memo} from 'react';
const View1 = memo(function View1(props){
console.log('我是view1页面->我也更新的')
return (<div>
<h3>我是view1页面</h3>
<p>接收到的名称:{props.name}</p>
</div>)
});

参考官方文档 :https://zh-hans.reactjs.org/docs/react-api.html#reactmemo

react-router

概述

如果您熟悉 JavaScript 生态系统、React 和 React Router,这可以作为 React Router v6 的快速概览,其中包含大量代码和最少的解释。

  • 有关 React Router 的完整介绍,请参阅教程
  • 有关每个 API 的大量文档,请参阅API 参考
  • 有关概念的更深入理解,请参阅主要概念

安装

1
npm install react-router-dom@6

配置路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { render } from "react-dom";
import {
BrowserRouter,
Routes,
Route,
} from "react-router-dom";
// import your route components too

render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Home />} />
<Route path="teams" element={<Teams />}>
<Route path=":teamId" element={<Team />} />
<Route path="new" element={<NewTeamForm />} />
<Route index element={<LeagueStandings />} />
</Route>
</Route>
</Routes>
</BrowserRouter>,
document.getElementById("root")
);

在以前版本的 React Router 中,当多个路由匹配一个不明确的 URL 时,您必须以某种方式对路由进行排序,以便获得正确的渲染。V6 更智能,并且会选择最具体的匹配,因此您不必再担心了。例如,URL/teams/new匹配这两个路由:

1
2
<Route path="teams/:teamId" element={<Team />} />
<Route path="teams/new" element={<NewTeamForm />} />

但是teams/new是比 更具体的匹配/teams/:teamId,所以<NewTeamForm />会渲染。

导航

用于Link让用户更改 URL 或useNavigate自己进行(例如在表单提交之后):

**请注意,react17版本中,定义变量use开头,如果不是hooks或者是高阶组件,将会报错 **

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { Link } from "react-router-dom";

function Home() {
return (
<div>
<h1>Home</h1>
<nav>
<Link to="/">Home</Link> |{" "}
<Link to="about">About</Link>
</nav>
</div>
);
}
import { useNavigate } from "react-router-dom";

function Invoices() {
let navigate = useNavigate();
return (
<div>
<p onClick={()=> navigate(`/invoices/${newInvoice.id}`)}
></p>
</div>
);
}

navLink取消了v5中的activeClassName

<navLink end></navLink> end添加到父级导航,作用是当子导航 被匹配时,父级导航 将不再高亮

<Link replace><NavLink replace> 替换方式

多级路由默认展示页

path=”about/*” ?

如果path=”about” react-router-dom只会走一级路由,不会进入二级路由匹配

如果path=”about/“ 同理,react-router-dom不会进入二级路由的匹配

path=”about/*”后 /about 会重定向到/about/tab1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Routes>
<Route path="/" element={<App></App>}>
<Route index element={<Message></Message>}></Route>
<Route path="about/*" element={<About></About>}>
<Route path="tab1" element={<AboutView1></AboutView1>}></Route>
<Route path="tab2" element={<AboutView2></AboutView2>}></Route>
<Route path="tab3" element={<AboutView3></AboutView3>}></Route>
<Route path="*" element={<Navigate to="tab1"></Navigate>}></Route>
</Route>
</Route>
<Route path="/404" element={<Error></Error>}></Route>
<Route path="*" element={<Navigate to="/404"></Navigate>}></Route>

</Routes>

路由配置表

配置表router.js

  • 子路由重定向404效果参考path:‘about/‘
  • 使用路由配置表时,不能再添加<Routes>组件
  • 使用useRoutes 时,需要要在<BrowserRouter>内,否则报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const routers = [
{
path:'message',
element:<Message/>
},
{
path:'about/*',
element:<About/>,
children:[
{
path:'tab1',
element:<AboutView1/>
},
{
path:'tab2',
element:<AboutView2/>
},
{
path:'tab3',
element:<AboutView3/>
},
{
path:'*',
element:<Navigate to="tab1"></Navigate>
}
]
},
{
path:'404',
element:<Error/>
},
{
path:'*',
element:<Navigate to="message"></Navigate>
}
]
export default routers;

index.js入口文件

1
2
3
4
5
6
7
8
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<App></App>
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);

App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function App() {
const element = useRoutes(RoutersList);
return (
<div className="app">
<h2>App.js入口文件</h2>
<hr/>
<main>
<nav></nav>
<section>
{element}
</section>
</main>
</div>
);
}

读取 URL 参数

  • useParams() 用于读取 /about/:id的参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import { Routes, Route, useParams } from "react-router-dom";

    function App() {
    return (
    <Routes>
    <Route
    path="invoices/:invoiceId"
    element={<Invoice />}
    />
    </Routes>
    );
    }

    function Invoice() {
    let params = useParams();
    return <h1>Invoice {params.invoiceId}</h1>;
    }
  • useSearchParams()用于读取?name=xxx&age=22 URL的参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <Link to="../tab3?name=shao.yuhong&age=22">跳转到tab3.jsx search方式</Link> 

    <!--
    * 读取参数
    * 请注意 useSearchParams是一个hooks函数
    -->
    import { useParams,useSearchParams,useLocation } from 'react-router-dom';
    const [searchParams,setSearchPamras] = useSearchParams();
    const name = searchParams.get('name'),age = searchParams.get('age')

    <!--也可以实现设置他当前url参数 ?加不加都无所谓-->
    setSearchPamras.set('name=xiaoliu&age=10000');
    setSearchPamras.set('?name=xiaoliu&age=10000');
  • useLocation() 用于读取原V5中的state,或者search(?name=xxx&age=22) 经过测试-》state传参页面刷新参数不会丢失

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     <Link to="../tab3" state={{name:'shao.yuhong',age:25}}>跳转到tab3.jsx state方式</Link>
    const search = useLocation();

    {/*
    结果如下:
    {pathname: '/about/tab3', search: '', hash: '', state: {…}, key: '9flq8e3f'}
    hash: ""
    key: "9flq8e3f"
    pathname: "/about/tab3"
    search: ""
    state: {name: 'shao.yuhong', age: 25}
    [[Prototype]]: Object
    */}

传参跳转

除了使用Link NavLink 传递参数,也可能使用编程式传参

1
2
3
4
5
6
7
8
<Link to="../tab3" state={{name:'shao.yuhong',age:25}}>跳转到tab3.jsx state方式</Link>
<Link to="../tab3?name=shao.yuhong&age=22">跳转到tab3.jsx search方式</Link>
<p onClick={
()=>navigateTo('../tab3?school=重庆师范大学',{state:{name:'shaoyuhong',age:22}})
}>P标签事件跳转</p>
<p onClick={
()=>navigateTo('../tab3?school=重庆师范大学',{replace:true,state:{name:'shaoyuhong',age:22}})
}>P标签事件跳转</p>

navigate提供的配置项

1
2
3
4
navigate(url,{
replace:false/true,
state:{},
})

除此之外

1
2
navigate(1);//前进
navigate(-1);//后退

嵌套路由

这是 React Router 最强大的功能之一,因此您不必弄乱复杂的布局代码。你的绝大多数布局都与 URL 的段耦合,React Router 完全接受了这一点。

路由可以相互嵌套,它们的路径也会嵌套(子继承父)。

1
2
3
4
5
6
7
8
9
10
function App() {
return (
<Routes>
<Route path="invoices" element={<Invoices />}>
<Route path=":invoiceId" element={<Invoice />} />
<Route path="sent" element={<SentInvoices />} />
</Route>
</Routes>
);
}

此路由配置定义了三个路由路径:

  • "/invoices"
  • "/invoices/sent"
  • "/invoices/:invoiceId"

**嵌套路由时,对应的父组件需要使用<Outlet/>标识需要展示的子组件的位置 **

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 <BrowserRouter>
<Routes>
<Route path="/" element={<App></App>}>
<Route path="about" element={<About></About>}></Route>
</Route>
<Route path="/404" element={<Error></Error>}></Route>
<Route path="*" element={<Navigate to="/404"></Navigate>}></Route>

</Routes>
</BrowserRouter>

<!-- App.js -->
import { Outlet } from 'react-router-dom';

function App() {
return (
<div className="App">
<h2>App.js入口文件</h2>
<hr/>
<Outlet/>
</div>
);
}

索引路线

索引路由可以被认为是“默认子路由”。当父路由有多个子路由,但 URL 就在父路由的路径上时,您可能希望将某些内容渲染到插座中。

子路由的默认展示 无需path路由, 通过设置index实现; <Route index element={<Activity />} />

考虑这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route path="invoices" element={<Invoices />} />
<Route path="activity" element={<Activity />} />
</Route>
</Routes>
);
}

function Layout() {
return (
<div>
<GlobalNav />
<main>
<Outlet />
</main>
</div>
);
}

此页面在“/invoices”和“/activity”中看起来很棒,但在“/”中它只是一个空白页面,<main>因为那里没有要渲染的子路由。为此,我们可以添加一个索引路由:

1
2
3
4
5
6
7
8
9
10
11
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Activity />} />
<Route path="invoices" element={<Invoices />} />
<Route path="activity" element={<Activity />} />
</Route>
</Routes>
);
}

现在在“/”处,<Activity>元素将呈现在插座内。

您可以在路由层次结构的任何级别拥有一个索引路由,当父级匹配但其他子级不匹配时将呈现该索引路由。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function App() {
return (
<Routes>
<Route index element={<Home />} />
<Route path="dashboard" element={<Dashboard />}>
<Route index element={<DashboardHome />} />
<Route
path="invoices"
element={<DashboardInvoices />}
/>
</Route>
</Routes>
);
}

相对链接

相对<Link to>值(不以 a 开头/)是相对于渲染它们的路径的路径。下面的两个链接将链接到/dashboard/invoices并且/dashboard/team因为它们在<Dashboard>. 当您更改父 URL 或重新排列组件时,这非常好,因为您的所有链接都会自动更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import {
Routes,
Route,
Link,
Outlet,
} from "react-router-dom";

function Home() {
return <h1>Home</h1>;
}

function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<nav>
<Link to="invoices">Invoices</Link>{" "}
<Link to="team">Team</Link>
</nav>
<hr />
<Outlet />
</div>
);
}

function Invoices() {
return <h1>Invoices</h1>;
}

function Team() {
return <h1>Team</h1>;
}

function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="dashboard" element={<Dashboard />}>
<Route path="invoices" element={<Invoices />} />
<Route path="team" element={<Team />} />
</Route>
</Routes>
);
}

“未找到”路线

当没有其他路由与 URL 匹配时,您可以使用path="*". 此路由将匹配任何 URL,但具有最弱的优先级,因此路由器仅在没有其他路由匹配时才会选择它。

1
2
3
4
5
6
7
8
9
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="dashboard" element={<Dashboard />} />
<Route path="*" element={<NotFound />} />
</Routes>
);
}

多组路由

尽管您应该只<Router>在应用程序中拥有一个,但您可以在``任何需要它们的地方拥有尽可能多的。每个<Routes>元素独立于其他元素运行,并选择一个子路由进行渲染。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function App() {
return (
<div>
<Sidebar>
<Routes>
<Route path="/" element={<MainNav />} />
<Route
path="dashboard"
element={<DashboardNav />}
/>
</Routes>
</Sidebar>

<MainContent>
<Routes>
<Route path="/" element={<Home />}>
<Route path="about" element={<About />} />
<Route path="support" element={<Support />} />
</Route>
<Route path="dashboard" element={<Dashboard />}>
<Route path="invoices" element={<Invoices />} />
<Route path="team" element={<Team />} />
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
</MainContent>
</div>
);
}

后裔<Routes>

你可以在任何你需要的地方渲染一个``元素,包括在另一个组件树的深处<Routes>。这些将与任何其他的一样工作<Routes>,除了它们将自动构建在渲染它们的路径的路径上。如果这样做,请*确保在父路由路径的末尾放置一个 **。否则,当父路由的路径长于父路由的路径时,父路由将不会匹配 URL,并且您的后代<Routes>将永远不会出现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="dashboard/*" element={<Dashboard />} />
</Routes>
);
}

function Dashboard() {
return (
<div>
<p>Look, more routes!</p>
<Routes>
<Route path="/" element={<DashboardGraphs />} />
<Route path="invoices" element={<InvoiceList />} />
</Routes>
</div>
);
}

仅此而已!我们没有在这里介绍所有 API,但这些绝对是您将使用的最常用的 API。如果您想了解更多信息,请继续学习我们的教程或浏览完整的 API 参考

其它杂项

useInRouterContext

useInRouterContext() 只要是在<browseRouter>中,该函数返回true

useNavigationType

NavigationType = “POP” | “PUSH” | “REPLACE”; 通过useNavigationType()返回当前的导航类型或用户如何来到当前页面

POP代表页面第一次刷新或者第一次进入时,返回POP

useResolvedPath

将字符串解析为URL

1
2
import { Link,useNavigate,useNavigationType,useResolvedPath } from 'react-router-dom';
useResolvedPath('?keyword=test#h1')

result:*{pathname: ‘/about/tab2’, search: ‘?keyword=test’, hash: ‘#h1’}*

​ hash**: “#h1”

​ pathname**: “/about/tab2”

​ search**: “?keyword=test”

​ [[Prototype]]: Object


  • 本文标题:react补充点
  • 本文作者:邵预鸿
  • 创建时间:2022-02-23 22:15:24
  • 本文链接:/images/logo.jpg2022/02/23/react补充点/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!