• 有问题请联系QQ:2374146085
  • 有问题请联系QQ:2374146085

React-新旧生命周期+路由v5v6

6个月前 (10-05) 467次浏览 已收录 0个评论 扫描二维码

导读

react新旧生命周期钩子函数和它们的区别、Fragment、组件优化PureComponent、renderProps插槽、ErrorBoundary错误边界处理,react路由v5和v6、reactRouter5、reactRouter6、解决多级路径刷新页面样式丢失的问题、路由传参、编程式导航、withRouter、路由懒加载、<Navigate> <NavLink>useRoutes路由表、嵌套路由Outlet、useNavigate、useInRouterContext、useNavigationType、useOutlet、UseResolvedPath.


生命周期函数

三个阶段

初始化阶段: 由ReactDOM.render()触发—初次渲染

  • constructor() 构造函数
  • componentWillMount()  组件将要挂载的钩子
  • render()    初始化渲染、状态每次更新之后 的钩子(去更新)
  • componentDidMount()  组件挂载完毕的钩子
    • 常用,常做一些初始化的工作,比如发起网络请求,定时器,订阅消息

更新阶段: 由组件内部this.setSate()或父组件重新render触发

  • shouldComponentUpdate()  控制组件更新的开关
  • componentWillUpdate()   组件将要更新的钩子
  • render() 必须用
  • componentDidUpdate()   组件更新后的钩子

卸载组件: 由ReactDOM.unmountComponentAtNode()触发

  • componentWillUnmount()  组件将要卸载的钩子
    • 常用,唱做一些收尾的工作,比如取消订阅

其他: componentDidCatch 组件渲染出错时的钩子

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="test"></div>

    <script src="../../lib/react.development.js"></script>
    <script src="../../lib/react-dom.development.js"></script>
    <script src="../../lib/babel.min.js"></script>

    <script type="text/babel">
        class C1 extends React.Component {

            constructor(props){
                super(props)
                console.log('C1---constructor')
            }

            state={
                count:0
            }

            //组件将要挂载的钩子
            componentWillMount(){
                console.log('C1---componentWillMount')
            }

            //组件挂载完毕的钩子
            componentDidMount(){
                console.log('C1---componentDidMount')
            }
            //组件将要卸载的钩子
            componentWillUnmount(){
                console.log('C1---componentWillUnmount')
            }
            //控制组件更新的开关 
            shouldComponentUpdate(){
                console.log('C1---shouldComponentUpdate')
                return true  //允许更新 
            }
            //组件将要更新的钩子
            componentWillUpdate(){
                console.log('C1---componentWillUpdate')
            }
            //组件更新后的钩子
            componentDidUpdate(){
                console.log('C1---componentDidUpdate')
            }


            //主动卸载指定组件
            fun_unMountByBtn=()=>{ 
                ReactDOM.unmountComponentAtNode(document.getElementById('test'))
            }
            //更新状态
            fun_addCount=()=>{ 
                const {count} = this.state
                this.setState({count:count+1})
            }
            //强制更新组件的 回调函数
            fun_force=()=>{
                this.forceUpdate()
            }
            

            //初始化渲染、状态每次更新之后 的钩子(去更新)
            render(){
                console.log('C1---render')
                return (
                    <div>
                        <h1>C1组件</h1>
                        <button onClick={this.fun_unMountByBtn}>卸载组件</button><br/>
                        <p>{this.state.count}</p>
                        <button onClick={this.fun_addCount}>count自增</button><br/>
                        <button onClick={this.fun_force}>不更新任何状态,强制更新一下组件</button>
                    </div>
                )
            }
        }
        
        //父组件A
        class A extends React.Component{
            state={
                name:'wxl'
            }

            fun_changeName=()=>{
                this.setState({name:'zzl'})
            }

            render(){
                return(
                    <div>
                        <p>C2父组件,我没有展示自己的state</p>
                        <button onClick={this.fun_changeName}>改名字</button>
                        <B name={this.state.name}/>
                    </div>
                )
            } 
        }
        //子组件B
        class B extends React.Component{

            //组件将要接收props时的钩子,第一次调用不触发该钩子,
            //也就是父组件setState更新状态,然后父组件就会render一次,继而给子组件传了props
            componentWillReceiveProps(props){
                console.log('B---componentWillRectiveProps',props)
            }
            // 控制组件更新的开关 
            shouldComponentUpdate(){
                console.log('B---shouldComponentUpdate')
                return true
            }
            //组件将要更新的钩子
            componentWillUpdate(){
                console.log('B---componentWillUpdate')
            }
            //组件更新后的钩子
            componentDidUpdate(){
                console.log('B---componentDidUpdate')
            }

            render(){
                console.log('B---render')
                return(
                    <div>
                        <p>C2子组件,我也可以展示父亲的name:{this.props.name}</p>
                    </div>
                )
            }
        }

        //渲染组件
        ReactDOM.render(<A />, document.getElementById('test'))
    </script>
</body>

</html>

新版本中把旧 生命周期钩子的三个钩子准备弃用了

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

因为官方认为会有开发者滥用,会对以后的异步渲染功能有影响,故新版本还对旧版本的这个钩子名字做了修改:分别在前面加上UNSAFE_ , 意思警告开发者这些是不安全的生命周期钩子

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="test"></div>

    <script src="../../lib/new/react.development.js"></script>
    <script src="../../lib/new/react-dom.development.js"></script>
    <script src="../../lib/new/babel.min.js"></script>

    <script type="text/babel">
        class C1 extends React.Component {

            constructor(props){
                super(props)
                console.log('C1---constructor')
            }

            state={
                count:0
            }

            static getDerivedStateFromProps(props,state){
                console.log('C1---getDerivedStateFromProps',props,state)
                // return props //会让state的值在任何时候都取决了props,并且不能被修改了,即获取一个衍生的对象
                return null
            }
            // 获取更新之前的快照
            getSnapshotBeforeUpdate(){
                console.log('C1---getSnapshotBeforeUpdate')
                return 'wxl'
            }
            //组件挂载完毕的钩子
            componentDidMount(){
                console.log('C1---componentDidMount')
            }
            //组件将要卸载的钩子
            componentWillUnmount(){
                console.log('C1---componentWillUnmount')
            }
            //控制组件更新的开关 
            shouldComponentUpdate(){
                console.log('C1---shouldComponentUpdate')
                return true  //允许更新 
            }
            //组件更新后的钩子
            componentDidUpdate(preProps,preState,snapshotValue){
                // preProps 先前的props
                // preState 先前的state
                // snapshotValue 快照值
                console.log('C1---componentDidUpdate',preProps,preState,snapshotValue)
            }


            //主动卸载指定组件
            fun_unMountByBtn=()=>{ 
                ReactDOM.unmountComponentAtNode(document.getElementById('test'))
            }
            //更新状态
            fun_addCount=()=>{ 
                const {count} = this.state
                this.setState({count:count+1})
            }
            //强制更新组件的 回调函数
            fun_force=()=>{
                this.forceUpdate()
            }
            

            //初始化渲染、状态每次更新之后 的钩子(去更新)
            render(){
                console.log('C1---render')
                return (
                    <div>
                        <h1>C1组件</h1>
                        <button onClick={this.fun_unMountByBtn}>卸载组件</button><br/>
                        <p>{this.state.count}</p>
                        <button onClick={this.fun_addCount}>count自增</button><br/>
                        <button onClick={this.fun_force}>不更新任何状态,强制更新一下组件</button>
                    </div>
                )
            }
        }
        
        //渲染组件
        ReactDOM.render(<C1 name="wxl"/>, document.getElementById('test'))
    </script>
</body>

</html>

三个阶段

初始化阶段:由ReactDOM.render()触发—初次渲染

  • constructor()
  • getDerivedStateFromProps
  • render()
  • componentDidMount() 常用

更新阶段: 由组件内部this.setSate()或父组件重新render触发

  • getDerivedStateFromProps
  • shouldComponentUpdate(nextProps,nextState)
  • render() 必用
  • getSnapshotBeforeUpdate
  • componentDidUpdate()

卸载组件: 由ReactDOM.unmountComponentAtNode()触发

  • componentWillUnmount() 常用

其他:

  • componentDidCatch 组件渲染出错时的钩子

新旧区别

  • 即将废弃三个钩子
  • 增加了两个新钩子(不常用)
    • getDerivedStateFromProps 了解即可,它会让state的值在任何时候都取决了props,并且不能被修改了
    • getSnapshotBeforeUpdate 获取组件更新之前的快照,即在最近一次渲染输出(提交dom节点)之前调用,他使得组件能在发生更改之前从dom中捕获一些信息(比如滚动位置),并且此声明周期的任何返回值将作为参数传递给componentDidUpdate,因为它是它的下游,因为一旦生命周期走到最后一个函数componentDidUpdate,那么就再也拿不到上一个环境下的信息了,此时就可以通过快照来先保存再传递给componentDidUpdate

脚手架

创建项目并运行

  • 第一步,全局安装:npm i -g create-react-app
  • 第二步,切换到想创项目的目录,使用命令:create-react-app hello-react
  • 第三步,进入项目文件夹:cd hello-react
  • 第四步,启动项目:npm start

react脚手架项目结构

public —- 静态资源文件夹

  • favicon.icon —— 网站页签图标
  • index.html ——– 主页面
  • logo192.png ——- logo图
  • logo512.png ——- logo图
  • manifest.json —– 应用加壳的配置文件
  • robots.txt ——– 爬虫协议文件

src —- 源码文件夹

  • App.css ——– App组件的样式
  • App.js ——— App组件
  • App.test.js —- 用于给App做测试
  • index.css —— 样式
  • index.js —— 入口文件
  • logo.svg ——- logo图
  • reportWebVitals.js
    • — 页面性能分析文件(需要web-vitals库的支持)
  • setupTests.js
    • —- 组件单元测试的文件(需要jest-dom库的支持)

扩展组件

Fragment

当不需要写key时,它和空标签一样的作用,都是为了不写根标签还不报错的功能.类似vue的<template>

但是当如果需要遍历时,就要使用Fragment标签,因为它可以加一个key属性.

import React,{Fragment} from 'react'

export default function App() {

  return (
    <Fragment>
      <>
       <p>wxl</p>
      </>
    </Fragment>
  )
}

组件优化

Component的两个问题

  • 只要调用了setState,虽然什么也没有修改,但是还是AB都会调用一次render
  • 当前组件重新render(),就会自动重新render子组件,纵使子组件没有用到父组件的任何数据,也会重新render,效率就比较低.

例如这样(错误示范)

import React, { Component } from 'react'

export default class A extends Component {

  state={name:'wxl',age:19}

  fun_change=()=>{
    // 只要调用了setState,虽然什么也没有修改,但是还是AB都会调用一次render
    this.setState({})
  }

  render() {
    console.log('A被调用一次')
    return (
      <>
        <div>A</div>
        <button onClick={this.fun_change}>虚晃一枪</button>
          <B/>
      </>
    )
  }
}
class B extends Component {
  render() {
    console.log('B被调用一次')
    return (
      <>
        <div>B</div>
      </>
    )
  }
}

造成这样的原因是Component中的shouldComponentUpdate()总是返回true.

效率高的做法PureComponent

  • 只有当父组件的state或props数据发生改变时才会重新render()
  • 他是利用了生命周期里的组件更新阀门功能来判断新旧两值来是否更新,需要程序员自己写.
import React, { Component } from 'react'

export default class A extends Component {

  state={name:'wxl',age:19}

  fun_change=()=>{
    this.setState({}) //什么也没有修改
  }

  shouldComponentUpdate(nextProps,nextState){
    console.log(this.props,this.state)
    console.log(nextProps,nextState)
    if(nextState.name === this.state.name) return false //父组件只会render一次
    else return true
  }

  render() {
    console.log('A被调用一次')
    return (
      <>
        <div>A</div>
        <p>{this.state.name}</p>
        <button onClick={this.fun_change}>虚晃一枪</button>
          <B/>
      </>
    )
  }
}
class B extends Component {

  render() {
    console.log('B被调用一次')
    return (
      <>
        <div>B</div>
      </>
    )
  }
}

上边的这种做法不推荐,实际上这是Component的问题,所有我们只需要使用PureComponent,它会自动判断.它的原理还是shouldComponentUpdate()

import React, { PureComponent } from 'react'

export default class A extends PureComponent {

  state={name:'wxl',age:19}

  fun_change=()=>{
    this.setState({}) //什么也没有修改
  }

  render() {
    console.log('A被调用一次')
    return (
      <>
        <div>A</div>
        <p>{this.state.name}</p>
        <button onClick={this.fun_change}>虚晃一枪</button>
          <B/>
      </>
    )
  }
}
class B extends PureComponent {

  render() {
    console.log('B被调用一次')
    return (
      <>
        <div>B</div>
      </>
    )
  }
}

  • PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
  • 只是进行state和props数据的浅比较(针对地址), 所以如果只是数据对象内部数据变了, 返回false,只有引用地址不同,才会返回true
  • 所以不要直接修改state数据, 而是要产生新数据,例如数组和对象.

renderProps(类似vue插槽)

向组件内部动态传入带内容的结构(标签)
  • Vue中:使用slot技术, 即通过组件标签体传入结构 <A><B/></A>
  • React中有两种
    • 使用children props: 通过组件标签体传入结构(此方法B组件拿不到A组件内的数据)
    • 使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性,因为render函数就代表返回一个组件
import React, { PureComponent } from 'react'

export default class A extends PureComponent {

  render() {
    console.log('A被调用一次')
    return (
      <>
        <div>A</div>
          {/* 对B组件预留好的位置进行C组件的插槽 ,
          其中name可以对插槽组件传值,由插槽位置B传过来 */}
          <B render={(name)=><C name={name}/>}/>
      </>
    )
  }
}
class B extends PureComponent {

  state={B_name:'B_wxl'}

  render() {
    return (
      <>
        <div>B</div>
          {/* B组件预留一个位置,来接受render属性返回函数的组件,
          对该组件进行传值name,类似vue的插槽 */}
        {this.props.render(this.state.B_name)}
      </>
    )
  }
}
class C extends PureComponent {

  render() {
    return (
      <>
        <div>C</div>
        {/* 此时就能接收到父组件传过来的值 */}
        <p>C:{this.props.name}</p>
      </>
    )
  }
}

项目打包运行

  • npm run build 项目打包
  • npm i serve -g 全局安装服务以便本地测试运行打包文件
  • serve build 运行所在目录下的build包文件

ErrorBoundary错误边界

需要在容易发生错误的组件的父组件做错误边界处理

只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
import React, { Component } from 'react'

export default class Parent extends Component {

  state={
    hasError:'' //用于标识子组件是否产生错误
  }

  //当Parent的子组件出现错误,会触发getDerivedStateFromError的调用,并携带错误信息,
  //把错误对象return出去,右因为getDerivedStateFromError和它没有return null的条件下,
  //所以此时状态state就会取决于这个错误对象了,并且状态不能被修改了,
   //类似新增的生命周期钩子getDerivedStateFromProps
  static getDerivedStateFromError(error){
    return {hasError:error}
  }

  //组件渲染出错时的钩子
  componentDidCatch(){ //在这里可有可无
    console.log('渲染组件出错,可以用在统计错误次数')
  }

  render() {
    return (
      <div>
        <p>Parent</p>
        {this.state.hasError ? <h1>子组件出现了错误</h1> : <Child/>}
      </div>
    )
  }
}

class Child extends Component {

  // state={names:['wxl','ll']}
  state={name:'wxl'} //此时父组件不做错误边界处理,会因为子组件一个小错而导致父组件乃至整个组件不能正常显示
  render() {
    return (
      <div>
        <p>Child</p>
        {/* 该子组件会报错 */}
        {this.state.names.map((item,index)=>{
          return <h2 key={index}>{item}</h2>
        })}
      </div>
    )
  }
}

路由v5

内置组件

  • <BrowserRouter>.  ‘com/a/b’
  • <HashRouter>.       ‘com/#/a/b
    • 开始时可以给index的App组件包一个组件(上方两个其中一个)
  • <Route> 注册路由,相当于if语句,条件成功,匹配相应组件,(全部匹配)
    • 若标签添加属性exact={true},开启严格匹配(url: /a !== /a/b/c 层面) 一般不开启
  • <Redirect> 代表重定向,也可以认为没有匹配到任何路由时候,就会匹配重定向
  • <Link>     普通路由链接
<Link className="test" to="/about">
      About
</Link>
默认是push模式,<Link replace={true} ...> 则是replace模式
  • <NavLink>
    • 适用于导航栏切换,并且该组件标签有一个activeClassName=”active” 键值对,可以负责高亮
 <NavLink activeClassName='active' className="test" to="/about">
        About
</NavLink>
    • 由于上方这样写会有代码重复性的风险,所以可以通过组件传值封装一个自己MyNavLink的一般组件,<MyNavLink to=”about”>about</MyNavLink>
    • ———————示例——————–
import React, { Component } from 'react'
import {Route } from 'react-router-dom'

import Home from './components/Home'
import About from './components/About'
import MyNavLink from './components/MyNavLink'

export default class App extends Component {
  render() {
    return (
      <div>
        <h2>React Router Demo</h2>
          <div>
            <div>
             {/* 路由链接 */}
             <MyNavLink to="/about">About</MyNavLink>
             <MyNavLink to="/home">Home</MyNavLink>
            </div>
            <div className="panel-body">
              {/* 注册路由以及路由view,类似 vue里的roter-view */}
              <Route path="/about" component={About}></Route>
              <Route path="/home" component={Home}></Route>
            </div>
          </div>
      </div>
    )
  }
}
import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'

export default class index extends Component {
  render() {
    return (
        // 因为父子组件中<MyNavLink>???</MyNavLink>中???是标签体内容,
        //官方是通过this.props.children属性传给了子组件
      <NavLink activeClassName="active" className="list-group-item" {...this.props}/>
    )
  }
}
  • <Switch> 单一匹配 , 防止route全部匹配
<Switch>
      <Route path="/about" component={About}></Route>
      <Route path="/home" component={Home}></Route>
      <Redirect to="/about"/> 代表重定向,也可以认为上方没有匹配到任何路由,就会走到重定向
</Switch>

用Switch包裹着,那么在以后的路有匹配中,匹配成功后,就不会再去下边路有匹配了

二级路由

需要开启子路由的组件

import React, { Component } from 'react'
import {Switch,Route,Redirect} from 'react-router-dom'

import News from './News/'
import Message from './Message'
import MyNavLink from '../../components/MyNavLink'

export default class index extends Component {
  render() {
    return (
        <div>
            <h3>我是Home的内容</h3>
            <MyNavLink to="/home/news">news</MyNavLink><br />
            <MyNavLink to="/home/message">message</MyNavLink>

            <Switch>
                <Route path="/home/news" component={News}></Route>
                <Route path="/home/message" component={Message}></Route>
                <Redirect to="/home/news"></Redirect>
            </Switch>
        </div>
    )
  }
}

解决多级路径刷新页面样式丢失的问题

  • public/index.html 中 引入样式时不写 ./ 写 / (常用)
  • public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用)
  • 使用HashRouter路由传参

params

// 向路由组件传递params
<Link to={`/home/message/${obj.id}/${obj.title}`}>链接</Link>
// --------------------------
//声明接收
<Route path="/home/message/:id/:title" component={Message}></Route>
// ---------------------
// Message可以通过this.props.match.params接收

search(query)

import qs  from 'querystring' //react默认带有 query字符串转对象的库
qs.stringify(obj)  //对象转字符串 
qs.parse(str)     //字符串转对象
路由组件传递search
<Link to={`/home/message/?id=001&name=wxl`}>链接</Link>
// --------------------------
//search不需要声明接收
<Route path="/home/message" component={Message}></Route>
// ---------------------
// Message可以通过this.props.location.search接收
const

state

这样传参不会在地址栏上显示,并且在BrowserRouter模式下,刷新后数据不会丢失.而在v5版本下的HashRouter模式会丢失.

// 向路由组件传递state,类型必须是对象
<Link to={{pathname:'/home/message',state:{id:'001',name:'wxl'}}}>链接</Link>
// --------------------------
//state不需要声明接收
<Route path="/home/message" component={Message}></Route>
// ---------------------
// Message可以通过this.props.location.state接收

编程式导航

fun_push=()=>{
    this.props.history.push('/home/news')
    // this.props.history.push('/home/news/${id}/${name}') params
    // this.props.history.push('/home/news?id=001&name=wxl')   query
    // this.props.history.push('/home/news',{id:'001',name:'wxl'})  state
}

withRouter 内置函数

可以加工一般组件,让一般组件具备路由组件所特有的API对象.

import {withRouter} from 'react-router-dom'

class Header extends Component{
...
}
export default withRouter(Header) //返回值是一个新组件

路由懒加载

import React, { Component ,lazy,Suspense} from 'react'
import {Route } from 'react-router-dom'

// import Home from './components/Home'
// import About from './components/About'
// import MyNavLink from './components/MyNavLink'

const Home = lazy(()=>import('./components/Home'))
const About = lazy(()=>import('./components/About'))
const MyNavLink = lazy(()=>import('./components/MyNavLink'))
// 路由懒加载必须配合Suspense一起使用

export default class App extends Component {
  render() {
    return (
      <div>
        <h2>React Router Demo</h2>
          <div>
            <div>
             {/* 路由链接 */}
             <MyNavLink to="/about">About</MyNavLink>
             <MyNavLink to="/home">Home</MyNavLink>
            </div>
            <div className="panel-body">
              <Suspense fallback={<h1>加载组件中...(此处要是loading组件的话,就不能对loading组件使用懒加载了,否则死循环)</h1>}>
                  <Route path="/about" component={About}></Route>
                  <Route path="/home" component={Home}></Route>
              </Suspense>
            </div>
          </div>
      </div>
    )
  }
}

其它

点击路由,会显示相应的组件,并且该组件会接收到以下路由传的参数,由this.props获取

也只有路由组件才有这些对象,除非使用withRouter

  • history对象
    • go(n) +n前进步 -n后退n步
    • goBack 后退
    • goForward 前进
    • push
    • repalce
  • location对象
    • pathname 当前路由路径 ‘/about’
    • search 路由传参(query)
    • state 路由传参(地址栏不显示)
  • match
    • params 路由传参

  • 一般组件 <Demo/>
  • 路由组件<Route path=”/about”component={About}></Route>

路由v6-默认版本

<Routes>与<Route>

  • Routes替换了v5的Switch
  • 两者要配合使用,且必须Routes包裹着Route
  • <Route caseSensitive> 匹配时不区分大小写
  • 当URL发生变化时,Routes都会查看其所有子Route元素,以找到最佳匹配并呈现组件.
  • <Route>也可以嵌套使用,也可以配合useRoutes()配置路由表,不过需要<Outlet>组件来渲染嵌套路由,具体解释后边会说.

<Navigate>

只要<Navigate>组件被渲染,就会修改地址栏路径,切换视图. 有两个参数:to 和 replace/push(默认)

import React from 'react'
import {NavLink,Route,Routes,Navigate} from 'react-router-dom'

import Home from './pages/Home'
import About from './pages/About'

export default function App() {
  return (
    <div>
      <h2>React Router Demo</h2>

      <NavLink to="/about">About</NavLink>
      <NavLink to="/home">Home</NavLink>

      {/* 注册路由以及路由view */}
      {/* v5.Switch === v6.Routes */}
      <div>
      <Routes>
        <Route path='/about' element={<About/>}/>
        <Route path='/home' element={<Home/>}/>
        {/* v5的<Redirect to='/home'/>重定向 等价于下边代码 */}
        <Route path='/' element={<Navigate to="/home"/>}/>
      </Routes>

      </div>
    </div>
  )
}

NavLink

与v5相比,该组件的自定义高亮样式方法改变了,如下代码,但是默认active类名的用法没有改变,还是直接定义好类名,该组件高亮会自动调用该类名.它有一个end属性,代表父路由与 子路由只能高亮一个.

import React from 'react'
import {NavLink,Route,Routes} from 'react-router-dom'

import Home from './pages/Home'
import About from './pages/About'

export default function App() {
  function fun_toggleClass({isActive}){
    return isActive ? 'class1 activeWXL' : 'class1'
  }
  return (
    <div>
      <h2>React Router Demo</h2>

      <NavLink className={fun_toggleClass} to="/about">About</NavLink>
      <NavLink className={fun_toggleClass} to="/home">Home</NavLink>

      <div>
      <Routes>
        <Route path='/about' element={<About/>}/>
        <Route path='/home' element={<Home/>}/>
      </Routes>

      </div>
    </div>
  )
}

useRoutes路由表

import React from 'react'
import {NavLink,Navigate,useRoutes} from 'react-router-dom'

import Home from './pages/Home'
import About from './pages/About'

export default function App() {

  //放在routes文件夹里
  const elements = useRoutes([
    {
      path:'/about',
      element:<About/>
    },
    {
      path:'/home',
      element:<Home/>
    },
    {
      path:'/',
      element:<Navigate to="/about"/>
    }
  ])
  return (
    <div>
      <h2>React Router Demo</h2>

      <NavLink to="/about">About</NavLink>
      <NavLink to="/home">Home</NavLink>

      <div>
      {/* <Routes>
        <Route path='/about' element={<About/>}/>
        <Route path='/home' element={<Home/>}/>
      </Routes> */}

      {/* 上下两方的代码效果功能一样 */}

      {elements}
      </div>
    </div>
  )
}

嵌套路由Outlet

类似vue里的route-view

例如,首先在一级组件里

import React from 'react'
import {NavLink,Navigate,useRoutes} from 'react-router-dom'

import Home from './pages/Home'
import About from './pages/About'
import News from './pages/News'

//放在routes文件夹里
export const elements = useRoutes([
  {
    path:'/about',
    element:<About/>
  },
  {
    path:'/home',
    element:<Home/>,
    children:[
      {
        path:'news',
        element:<News/>
      }
    ]
  },
  {
    path:'/',
    element:<Navigate to="/about"/>
  }
])

export default function App() {

  return (
    <div>
      <h2>React Router Demo</h2>

      <NavLink to="/about">About</NavLink>
      <NavLink to="/home">Home</NavLink>

      <div>
      {elements}
      </div>
    </div>
  )
}

然后发现Home有嵌套路由,所以需要在下一个嵌套组件Home里写好路由占位

import React from 'react'
import {Link , Outlet} from 'react-router-dom'

export default function Home() {
  return (
    <div>
      <p>Home</p>
      {/* to也不用写父级路由名字,并且不能写’/‘ */}
      <Link to="news">new</Link>
      <div>
        {/* 指定路由组件呈现的位置 */}
        <Outlet/>
      </div>
    </div>
  )
}

路由传参

params

使用useParams接收路由参数

  1. 定义接收路由参数的路由表
{
   path:'/home/:id/:name',
   element:<Home/>
},
  1. 传递参数
<NavLink to={`/home/001/wxl`}>Home</NavLink>
  1. 接收路由参数
import React from 'react'
import {useParams} from 'react-router-dom'

export default function Home() {
  const {id,name} = useParams()
  return (
    <div>
      <p>Home</p>
      <p>id:{id}</p>
      <p>name:{name}</p>
    </div>
  )
}

search/query

使用useSearchParams接收路由参数,也可以使用useMatch(相当于v5的match)来接收

  1. 定义接收路由参数的路由表
{
      path:'/home',
      element:<Home/>
    },
  1. 传递参数
<NavLink to={`/home/?id=001&name=wxl`}>Home</NavLink>
  1. 接收路由参数
import React from 'react'
import {useSearchParams} from 'react-router-dom'

export default function Home() {
  const [search,setSearch] = useSearchParams()
  const id=search.get('id')
  const name=search.get('name')
  return (
    <div>
      <p>Home</p>
      <p>id:{id}</p>
      <p>name:{name}</p>
    </div>
  )
}

state参数

使用useLocation接收路由参数

  1. 定义接收路由参数的路由表
{
      path:'/home',
      element:<Home/>
    },
  1. 传递参数
<NavLink to={`/home`} state={{id:'001',name:'wxl'}}>Home</NavLink>
  1. 接收参数
import React from 'react'
import {useLocation} from 'react-router-dom'

export default function Home() {
  const {state:{id,name}} = useLocation()
  return (
    <div>
      <p>Home</p>
      <p>id:{id}</p>
      <p>name:{name}</p>
    </div>
  )
}

编程式导航

借助useNavigate

import React from 'react'
import {useRoutes,useNavigate} from 'react-router-dom'

import {routes} from './routers/index'

export default function App() {
  const elements = useRoutes(routes)

  const navigate = useNavigate()
  function go(){
    navigate('/home')

    navigate('news') 跳转嵌套路由

    navigate('/home?id=001') //search
    navigate('/home/001') //params

    navigate('/home',{. //state
      replace:false,
      state:{
        id:'001',
        name:'wxl'
      }
    })

    navigate(1) //前进一位
    navigate(-1) //后退一位
  }
  return (
    <div>
      <h2>React Router</h2>

      <button onClick={go}>go</button>

      <div>
      {elements}
      </div>
    </div>
  )
}

其他hooks

useInRouterContext()

可以在组件里获取useInRouterContext() 返回true代表是在路由的上下文中,否在不在

useNavigationType()

  • 返回当前的导航类型(用户是如何来到当前页面的)
  • 返回值:POP(直接打开了这个路由组件)、PUSh、REPLACE

useOutlet()

  • 用来呈现当前组件中渲染的嵌套路由
  • 如果嵌套路由没有挂载,热返回null,否在返回嵌套的路由对象

useResolvedPath()

给定一个URL地址,解析其中的path、search、hash值,作为对象包裹返回


渣渣龙, 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:React-新旧生命周期+路由v5v6
喜欢 (0)

您必须 登录 才能发表评论!