【React】路由,Hooks

❤️ Author: 老九
☕️ 个人博客:老九的CSDN博客
🙏 个人名言:不可控之事 乐观面对
😍 系列专栏:

路由

在这里插入图片描述

react-router的基本使用

react-router最主要的API是给我们提供了一些组件
BrowserRouter使用history模式
HashRouter使用hash模式

用这两个组件将App组件进行包裹
在这里插入图片描述
在Router5.x版本,使用的是Switch组件
在App组件中(注意不是index.js文件中),Router5.x使用的Routes组件,Routes组件中使用一个一个的Route
Route有path和element两个属性,Router5.x中属性是component,6.x中使用的是element,element传入的是组件的实例
在这里插入图片描述

路由跳转

  • 通过Link组件就可以点击跳转
    在这里插入图片描述

NavLink

通过NavLink,就是Link带了一个class:active
在这里插入图片描述
如果要是自定义样式的话,style属性里面传入一个函数
在这里插入图片描述
这里的style要区别于元素的style,元素的style是传入一个对象,而组件的style传入的是一个函数
在这里插入图片描述
style传入的函数,参数是一个对象,这个对象可以解构出isActive

  • 如果想修改默认的类名
    在这里插入图片描述
    在这里插入图片描述

补充:
在这里插入图片描述
如果要把那个函数提出来,不需要写成箭头函数,直接this.getActiveClass就可以了,因为写成箭头函数的目的是如果getActiveClass函数中使用了this,才通过箭头函数获取到最外层的this,否则是不需要加箭头函数的

Navigate导航组件

  • 用于路由重定向,当这个组件出现时,就会执行跳转到对应的to路径中
    在这里插入图片描述
    也可以直接重定向
    在这里插入图片描述

NotFound

在这里插入图片描述

路由的嵌套

第一步:在App.jsx中写定义好路由的嵌套映射关系
在这里插入图片描述
第二步:在home组件中写link表示跳转到哪,注意需要一个outlet占位符组件
在这里插入图片描述

useNavigate

  • 通过useNavigate的hook函数实现手动跳转,因为link组件就是一个a标签,我们用useNavigate可以实现自定义标签的跳转了
  • 注意hook只能在函数组件中使用,并且hook函数必须写在代码的最上面
    在这里插入图片描述
    整体代码:
    在这里插入图片描述

在类组件中使用useNavigate

如果需要在类组件中用useNavigate,需要通过高阶组件进行拦截封装
下面的代码是在home类组件中添加一个歌单的按钮,进行跳转,也就是home组件中可以跳转到homesongmenu组件
在这里插入图片描述
首先写一个高阶组件,返回的是一个函数组件,因为只有在一个函数组件中,才能使用hook函数中的useNavigate,然后将这个navigate通过参数的方式传给类组件,类组件通过props接受这个router
在这里插入图片描述
在这里插入图片描述
总体代码

路由传递参数

通过传入的id进行页面跳转

通过动态路由
在这里插入图片描述

在类组件中,通过高阶函数使用useNavigate,实现页面的跳转
在这里插入图片描述
在这里插入图片描述
总体代码(最后一行没截全):
在这里插入图片描述

获取url上的参数

然后通过useParams高阶函数获取到路由参数上的值
在这里插入图片描述

在这里插入图片描述
通过useLocation或者ueSearchParams获取查询字符串
当出现跳转到这种页面的时候
在这里插入图片描述
在这里插入图片描述
通过useLocation获取问号后面的值
在这里插入图片描述
在这里插入图片描述
但是这样的话,取值的时候还需要自己处理字符串
如果通过useSearchParams的hook函数,就可以直接获取到参数的值
在这里插入图片描述
useSearchParams()函数返回一个数组,取值的时候,用get方法即可
在这里插入图片描述
也可以这样写:
在这里插入图片描述
返回的就是一个对象,更方便一些
在这里插入图片描述

route配置抽取

将原来写在App中content类下面的routes一大球改成使用hook函数的写法
在这里插入图片描述
建一个router的文件夹
在这里插入图片描述
在index.js中写入之前那一大球的routes,route等(只是换了一种写法)
在这里插入图片描述
在这里插入图片描述
最后将routes导出放在useRoutes的hook函数参数中即可

路由懒加载

在router包中的index.js文件中加载路由时通过React中的lazy函数实现路由懒加载
在这里插入图片描述
如果我们对某些组件进行了异步加载(懒加载),那么App组件需要使用Suspense进行包裹,fallback里面写的是如果异步加载没有加载完成的时候,先展示fallback里面的组件
在这里插入图片描述

Hook

  • 为什么需要Hook?让我们在不编写类组件的情况下使用state以及其他的React特性(生命周期)
  • 和类组件不同,函数式组件当我们修改state的时候,组件不知道重新进行渲染,并且生命周期也没有
  • rfce可以快速创建函数式组件 ,如果要包裹一个memo,就要用rmc
  • 使用hook的话只能在函数的顶层调用hook,只能在函数式组件中使用Hook,如果要在类组件中使用hook需要通过高阶函数

useState

  • useState小括号里传入初始值,返回一个数组,第一个是值,第二个是方法,数组解构
    在这里插入图片描述

使用useState

在这里插入图片描述
在这里插入图片描述

useState的原理

一般来说,在函数return退出后,变量就会消失,但是state中的变量会被React保留,当我们点击button的时候,执行了changeMessage函数,changeMessage中有一个setMessage函数,React内部实现了这个setMessag函数,传入了一个newMessage,React内部会保留下来这个newMessage,当我们调用了setMessage函数,React就会重新渲染这个函数组件(类似于重新执行render),当发现又执行了useState的,React就会把保存下来的newMessage给返回,展示到页面上
useState接受唯一一个参数,在第一次组件被调用时是用来作为初始化值,如果没有传递参数,那么初始化值是undefined

useEffect

  • Effect可以让你来完成一些类似于class中声明周期的功能,类似于完成网络请求,手动更新DOM,一些事件的监听
  • 通过useEffect的Hook,可以等待React页面渲染完成后执行某些操作,useEffect要求我们传入一个回调函数,在React执行完更新DOM操作后,就会回调这个函数
  • 注意这个hook是在函数组件每次渲染完成后,都会执行一次这个函数
  • 一个函数组件中可以有多个useEffect,是按照顺序执行的

下面是一个例子,根据count的值更改title
在这里插入图片描述

需要清除的Effect(componentWillUnmount)

比如我们之前都需要在componentWillUnmount中有对应的取消订阅,useEffect中的返回值是一个回调函数,在组件被重新渲染或者组件被卸载的时候执行
在这里插入图片描述

Effect性能优化

  • 因为每个Effect在组件每次重新加载都执行一次,但是如果需求是从服务器取数据,那么我们就取一次就可以,所以某些代码我们希望执行一次即可
  • useEffect可以传第二个参数,第二个参数是一个数组,根据数组里的值改变了,才重新执行回调函数

下面这个代码,数组里面什么都没写,就代表这个useEffect谁的影响都不受,只执行一次
如果第二个参数不传的话,就是无论什么情况,只要组件重新渲染,该useEffect就重新执行
在这里插入图片描述

补充useLayoutEffect和useEffect的区别

在这里插入图片描述
看下面的代码:
在这里插入图片描述
在这里插入图片描述
通过useLayoutEffect可以实现,在内容还没有显示在屏幕上之前,发现数据是不对的,我们就可以在这个hook函数中修改数据

例子

在这里插入图片描述

useContext

这个直接就更方便的使用contextAPI了
首先创建创建一个context文件夹,里面创建一个index.js 的文件,在index文件中创建两个context实例,并export出去
在这里插入图片描述
在这里插入图片描述
然后在root的index.js中,写上要提供的数据Provider
在这里插入图片描述
使用时候,不需要写什么consumer了,直接就使用usecontext,返回的值就是写在value中的值
在这里插入图片描述

useCallback

  • 问题:当我们重新调用函数组件的时候,函数组件中的函数就再次被定义了
  • useCallback会返回一个函数的记忆值,在依赖不变的情况下(根据第二个参数判断),多次定义的时候,返回的值是相同的
  • 当我们需要将一个函数传递给子组件的时候,最好使用useCallback进行优化,将优化之后的函数传递,防止重新渲染父组件的时候,子组件的函数也跟着重新调用

下面这个例子,当点击改变文本按钮时候,就会重新执行App组件,如果increment没有用useCallback包含,就会重新定义increment,而increment函数传递给了子组件,子组件的props发生改变,组件本身也会被重新渲染,所以子组件也跟着重新执行了一遍,所以需要将increment函数包裹在useCallback中,就可以避免子组件重新渲染的bug在这里插入图片描述
在这里插入图片描述

进一步优化

上面使用usecallback,解决了如果重复渲染父组件,其中父组件的函数传递到了子组件,子组件也导致重新渲染的问题。
接下来我们要优化的是,当count发生改变的时候,使用同一个函数,而不是每次都定义一次函数。

方法一

将数组中count的依赖移除掉,虽然都是使用同一个函数,但是会导致闭包陷阱的问题,每次传入的count都是useState的初始值
在这里插入图片描述

闭包陷阱,如果上面的代码数组中不传入count,就会一直用第一个函数,相当于下面的bar1,等同于useCallback参数中接受到的count一直都是0
在这里插入图片描述

方法二

使用useRef,这个hook在组件多次渲染时,返回同一个值
我们通过将count变量赋值给了contRef的current属性上,从而就解决了闭包陷阱的问题,每次传入的count值就是增加过的值,通过useRef返回的都是同一个值
在这里插入图片描述

useMemo

  • useMemo优化的是函数的返回值。而useCallback优化的是函数的本身
  • useMemo接受一个回调函数,返回一个函数计算后的值,useCallback接受一个函数,返回一个优化后的函数
  • 类似于vue的computed,对于某一个属性的操作比较复杂,而每次重新渲染函数组件的时候,重新计算复杂的数据就会影响性能,通过useMemo就可以判断如果数据没有发生变化,就使用之前的数据就可以了
    在这里插入图片描述
    usememo的第二个作用是在向子组件传递对象的时候,可以包一个usememo,对于普通的变量,如果数值没有发生改变,父组件重新渲染,子组件也不会重新渲染,但是对于对象来说,父组件重新定义了对象,传递给子组件后,即使对象里的值没有发生改变,子组件也会重新渲染,因此使用usememo进行包裹
    在这里插入图片描述

完整代码:

import React, { memo, useMemo, useState } from 'react';

const HelloWorld = memo(function (props) {
  console.log("Hello world 被渲染");
    return <h2>Hello World</h2>
})

function calcNumTotal (num) {
  // console.log("计算过程重新调用");
  let total = 0
  for (let i = 1; i < num; i++){
    total += i
  }
  return total
}

const App = memo(() => {
  const [count,setCount] = useState(0)

  let result = useMemo(() => {
    return calcNumTotal(count)
  }, [count])

  // const info = { name: "why", age: 18 }
  const info = useMemo(()=>({name:"why",age:18}),[])

  return (
    <div>
      <h2>计算结果: {result}</h2>
      <h2>计数器:{count}</h2>
      <button onClick={e => setCount(count + 1)}>+1</button>

      <HelloWorld result={result} info={info} />
    </div>
  )
})

export default App

usememo和usecallback的异同

下面这段代码的increment和increment2是等同的
在这里插入图片描述
官方文档中是这么说的
在这里插入图片描述
通常使用useCallback的目的是不希望子组件进行多次渲染,并不是为了函数进行缓存

useRef

useRef返回一个ref对象,返回的ref对象在组件的整个生命周期保持不变

用法一:获取DOM

在这里插入图片描述

用法二:返回的总是同一个对象

通过这个用法,就可以解决usecallback的闭包陷阱了
在这里插入图片描述

自定义Hook

  • 自定义Hook本质上只是一种函数代码逻辑的抽取,hook本身就是一个函数
  • 注意:自定义函数的名字必须用use开头

下面举个例子,抽取useLogLife函数,下面代码中useEffect的数组依赖中可以写个cName,就不报黄色波浪线了
在这里插入图片描述

抽取context

首先文件的目录结构是这样的
在这里插入图片描述
使用createContext创建context
在这里插入图片描述
外层index.js中用Context.Provider,为对应的context提供数据
在这里插入图片描述
创建hooks文件夹后,抽离使用context的逻辑
在这里插入图片描述
在hooks的index.js文件中导出自定义的hook
在这里插入图片描述

在app.js中使用自定义Hook
在这里插入图片描述

监听滚动位置

监听的逻辑属于函数组件的副作用,除了渲染页面的逻辑,其他都属于副作用,类似axios请求数据,监听滚动页面等
因此监听的逻辑需要放在useEffect中,并且不依赖任何变量,只执行一次,useEffect后面的数组参数什么都不写

自定义hook
在这里插入图片描述
使用自定义hook
在这里插入图片描述

自定义LocalStorage

写localStorage的自定义hook
在这里插入图片描述
使用localstorage的自定义hook
在这里插入图片描述

————————————————————————
♥♥♥码字不易,大家的支持就是我坚持下去的动力♥♥♥
版权声明:本文为CSDN博主「亚太地区百大最帅面孔第101名」的原创文章

  • 19
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李小浦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值