react总结

news2024/9/30 11:25:59

一、React 入门

1.1 特点

高性能、声明式、组件化、单向响应的数据流、JSX扩展、灵活

1.2 React初体验

<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>01.React初体验</title>
    <!--1、引入react核心文件-->
    <script src="./js/react.development.js"></script>
    <!--2、引入react-dom文件-->
    <script src="./js/react-dom.development.js"></script>
</head>

<body>
    <!--3、创建容器元素-->
    <div id="root"></div>
    <script>
        //4、通过ReactDOM来创建挂载的根容器
        const root = ReactDOM.createRoot(document.querySelector('#root'));
        //5、通过root.render来指定呈现的内容
        root.render('尚硅谷');
    </script>
</body>

1.3 注意事项

  • root.render可以使用多次,但最后一次render会将之前的覆盖掉。
  • 不要同时挂载相同的容器(会报错)
  • 在一个页面中,可以挂载多个不同的容器
  • render中可以渲染的内容类型(数组类型、数值类型、字符串类型)
  • render中不可以渲染的内容类型,会报错 (对象和函数不能渲染,但是函数中可以return让render能渲染的内容类型,json对象字符串可以渲染)
  • render中不会报错,但不会渲染内容的类型
	 //5、通过root.render来指定呈现的内容
	 //5.1 undefined类型
	 //root.render(undefined);
	 //5.2 空
	 //root.render();
	 //5.3 null
	 //root.render(null);
	 //5.4 true
	 //root.render(true);
	 //5.5 false
	 root.render(false);
  • 不建议将根容器指定为body元素或html元素(会报错)

二、虚拟DOM

2.1 创建

语法:*React.createElement(标签名,标签属性对象,值)*

标签属性:需要传入一个对象,设置:键值对,不设置:空对象{}或者null

  • 注意:如果想要创建的标签中嵌套其他子标签时

语法:*React.createElement(标签名,标签属性,子节点1,子节点2...)*同级标签
*React.createElement(标签名,标签属性,React.createElement(标签名,标签属性.'内容')* 子标签

  • 包含多个元素:从第三个参数开始即是包裹的内容。或将第三个参数设置为数组,

    如果第三个参数设置为数组,数组元素需要增加唯一key属性

2.2 JSX语法

2.2.1 jsx基本语法

 //6、render可以接收虚拟DOM,会将虚拟DOM转为真实Dom
//6.1 渲染指定元素
// root.render(<div>我是一个DIV</div>)
一个元素包裹多个元素
如果元素是单标签,必须闭合
如果元素较多可以换行,建议这样做添加 ()
JSX中标签必须闭合,单双标签都必须
JSX有且只能是一个根元素
可以使用包裹标签:包裹标签不会被编译(不会渲染),只是起到包裹的作用。
<React.Fragment></React.Fragment>

2.2.2 注释

必须使用 {} 包裹

  • 单行注释 { // 我是注释 }
  • 多行注释 { /* 添加多行注释*/ }

2.2.2 插值表达式

语法 {}
{指定动态的属性值和标签体文本}中填写变量值。

  • 可以是任意基本类型数据值
    • 如果{}内的是用双引号,单引号,反引号包裹,则是字符串;
    • 如果{}内是数字,即是要输出该数字;
    • 如果{}内是undefined,null,true,false不会进行渲染
  • 可以是一个 JS 数组,会直接进行展开渲染(不包含逗号 ), 不允许是对象
  • 可以是 JS 的表达式,不允许在{}内写JS语句
  • 可以是 react 元素对象

2.3.4 条件渲染

在React中实现条件渲染的方式有以下三种:

  • if…else

    当判断的条件较多时,我们还可以采用函数的方式

  • 三元表达式

  • 逻辑短路

    注意: 只适用于只在一种情况下才有界面的情况(也就是说当条件满足时即有界面否则没有,并非二选一)

    • 表达式1 && 表达式2

      如果表达式1对应的boolean为true, 返回表达式2的值

      如果表达式1对应的boolean为false, 返回表达式1的值

    • 表达式1 || 表达式1

      如果表达式1对应的boolean为true, 返回表达式1的值

      如果表达式1对应的boolean为false, 返回表达式2的值

const root = ReactDOM.createRoot(document.querySelector("#root"));
const vDom = isLogin && <h2>恭喜您,登录成功</h2>;
root.render(vDom);

2.3.5 属性渲染

作用:为属性指定数据,可以直接使用{},不需要加双引号

  constimgUrl = "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png"
  const w = 110;
  <img src={imgUrl} width={w} />

2.3.6 列表渲染

react中可以将数组中的元素依次渲染到页面上,可以直接往数组中存储react元素,推荐使用数组的 map 方法

注意:必须给列表项添加唯一的 key 属性, 推荐使用id作为key, 尽量不要用index作为key

  • 数组是可以被直接展开的
  • 数组中的元素也可以是JSX
  • 如果使用map,map接收的是一个箭头函数,{}:如果要返回内容需要使用return,()返回的内容

2.3.7 样式渲染

  • 行内样式(style)
    style属性值是一个对象,
    带有"-"的属性名需要写成驼峰命名
    当属性值单位为px的时候,且值为number类型,可以省略单位,直接写成数值
    <div style={{width: 100,height: '100px', background: 'red',marginLeft: '100px'}}>尚硅谷</div>

  • 非行内样式(className)
    <p className="box">尚硅谷</p>

2.3.7 关于事件

  • 绑定事件

React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:

React 事件的命名采用小驼峰式(camelCase),而不是纯小写。比如:onClick、onFocus 、onMouseEnter

使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串

const div = <div onClick={事件处理函数}></div>

  • 事件对象

React 根据 W3C 规范来自定义的合成事件, 与原生事件不完全相同

  • 处理好了浏览器的兼容性问题

  • 阻止事件默认行为不能使用 return false, 必须要调用: event.preventDefault()

  • 有自己特有的属性, 比如: nativeEvent – 原生事件对象

  • <input>的 change 监听在输入过程中触发, 而原生是在失去焦点才触发

    原理:内部绑定的是原生 input 事件

在事件对象中属性虽然很多,但是一点要记住的有两点:

  • 1、实际发生事件的目标对象(由于最终虚拟DOM要转换成真实DOM,所以e.target获取的都是真实DOM):e.target
    console.log(e.target)
    console.log(e.target.innerHTML)
  • 2、关于事件默认行为的取消:e.preventDefault()

三、React组件

1.1 函数组件

  • 定义

    要求组件名称的首字母需要大写

    function 组件名称(){ 
    	// 返回一段jsx的视图结构 
    	return <div></div>
    }
    
  • 调用

    • <组件名称></组件名称>

    • <组件名称 />

  1. 组件名首字母必须大写. 因为react以此来区分组件元素/标签 和 一般元素/标签
  2. 组件内部如果有多个标签,必须使用一个根标签包裹.只能有一个根标签
  3. 必须有返回值.返回的内容就是组件呈现的结构, 如果返回值为 null,表示不渲染任何内容
  4. 会在组件标签渲染时调用, 但不会产生实例对象, 没有状态

1.2 类组件

  • 定义
class 类组件名 extends React.Component{
	render(){
		return <div></div>
	}
}
  • 解释

    • extends:继承
    • React.Component: react提供的一个类组件的基础类(父类)
  • 注意

    • render必须是一个方法
    • return的必须是组件类的视图结构
  • 特点

    • 类组件有自己的生命周期和状态数据
  • 调用

    • <组件名称></组件名称>

    • <组件名称 />

* 使用类式组件的注意事项:
* 1、组件名首字母大写
* 2、每一个类式组件的继承父类都是React.Component
* 3、类式组件中必须要有render方法,通过render方法来进行返回虚拟DOM元素对象
* 4、类式组件调用时相当于ES6中的new ()的写法,产生组件实例化对象,
*    在组件实例化对象中可以使用像state、props、ref等属性
* 5、在return返回的虚拟DOM元素对象中必须要有一个绝对唯一的根标签,
     如果在return后面换行书写,需要添加一个()
* 6、想要给组件实例化对象身上添加初始化的属性,则可以使用constructor构造器,
     如果没有明确写出,则系统会默认分配一个空的构造器
     如果明确写出构造器,则在构造器中的第一句话需要调用super()方法
* 7、如果类中既有constructor,又有render方法,
     则先执行constructor,在执行render,且这两个方法都是自动执行
* 8、其中的this均指向类组件的实例化对象

四、React组件实例的三大核心属性

2.1 state

值是一个对象(可以包含多个key-value)

基本使用

  • 初始化 state 两种方法

    • 构造器中: this.state = {xxx: 2}
    • 类体中: state = {xxx: 2}
  • 读取state数据

    • this.state.xxx
  • 更新state数据

    • 不能直接更新state数据
    • 必须 this.setState({ 要修改的属性数据 }) 异步方法
  • state中的事件绑定

注意:

  1. 在虚拟DOM元素身上绑定事件的时候,事件名遵循小驼峰写法

  2. 事件函数直接使用{函数名}的写法,不能像原生JS中写入一个字符串,

    而且也不要在{}中直接调用函数,这样会脱离事件,页面加载后直接运行函数内容。

  • 类中方法的this

问题: 类中定义的事件回调方法中thisundefined, 无法更新state

原因:

事件回调都不是组件对象调用的, 都是事件触发后,直接调用的,

class中所有方法都使用严格模式, 所以方法中的this就是undefined

解决办法1 - 包裹箭头函数
原因: render中的this是组件对象, 处理函数是我们通过组件对象来调用的

解决办法2 - bind绑定this
原因: 构造器中的this是组件对象, 将处理函数通过bind绑定为了组件对象

解决办法3 - 箭头函数
原理: 利用bind给事件回调绑定this为组件对象(render中的this)

选择:

  1. 一般用箭头函数方式, 编码简洁
  2. 如果要传递特定的数据, 选择用 包裹箭头函数方式
    问题1:如果state中的不光有isGood一个属性状态,那么在修改的时候属性是合并还是替换呢??

答:

state中进行修改状态的时候,其实是一种合并属性,而并不是替换属性;

说白了就是替换同名的属性,不同名的仍然保留。

问题2:类中的构造器被调用了几次??

答:

当new一个类的时候,就会产生一个实例对象,当在new的过程中代码中出现了构造器就会执行,

所以构造器执行了几次,需要看页面中有几个类的组件实例,有几个组件实例,那么构造器就会执行几次。

问题3:类中的render被调用了几次??

答:1 + n次

1次是初始化时候的次数,n是状态更新的次数

问题4:类中的changeMood被调用了几次??

答:事件发生几次,这个函数就会执行几次

问题5:类中添加构造器的作用是什么??

答:需要借助构造器进行初始化状态,还可以解决this指向问题

2.2 props

  • 介绍

props 是 『properties』 的缩写, 也是类式组件实例对象的属性

  • 作用

如果想要在组件的外部向组件的内部传数据,那么就可以利用props

  • 语法: <组件 属性名=“属性值”/>

    在类组件中使用this.props.data就可以取得属性名中的值
    在函数组件中使用props.data就可以取得属性名中的值

    注意:其中属性名中可以任意指定但是组件中的和获取props要对应好了

  • 批量传递props 使用...扩展运算符
    语法:<组件 {...obj}/>

2.2.1 props中的children属性

props.children可以获取组件标签中间的内容

//单标签
<Student {...item} } children =/>
//双标签
<Student>值 通过props.children获取<Student/>

2.2.2 props 的特点

  • 可以给组件传递任意类型的数据

  • props 是只读的对象,只能读取属性的值,不要修改props

  • 可以通过...运算符来将对象的多个属性分别传入子组件

  • 如果父组件传入的是动态的 state 数据, 那一旦父组件更新 state 数据, 子组件也会更新

2.3 refs

refs 提供了一种允许我们访问 DOM 节点或在 render 方法中创建的 React 元素的写法。

ref是虚拟DOM对象的一个属性。

作用:方便在组件中获取真实DOM对象

refs的语法场景有三种:字符串形式、回调函数形式、createRef形式

  • 字符串形式的ref【最简单,但是不推荐】

语法:<标签 ref=“名称”>
通过this.refs对象来获取值

  • 回调函数形式的ref【官方推荐】

语法:<标签 ref={currentNode => this.名称 = currentNode}>
获取:this.名称
回调函数中接受一个参数,参数名可以自定义,表示的是当前的节点对象。

ref的回调函数写法:ref={形式参数名=>this.属性名 = 形式参数名}
其中形式参数名在函数身上可以自定义,表达当前所在的虚拟DOM元素对象
this后面的属性名则直接添加在类组件实例化对象身上
ref属性只有值为字符串的形式的时候,才会需要使用this.refs的写法
除此之外后两种ref的用法都会直接添加在类的组件实例化对象身上

思考:ref的回调函数具体执行了几次??

摘自官方文档:

如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null

然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,

所以 React 清空旧的 ref 并且设置新的。

通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,

但是大多数情况下它是无关紧要的。

  • createRef的使用【官方最推荐的

语法:React.createRef()

<script type="text/babel">
         class Demo extends React.Component{
             /**
              * React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是"专人专用"的
              * **/
             myRef = React.createRef();// {current:null}
             myRef2 = React.createRef();
             render(){
                 return (
                     <div>
                        <input ref={this.myRef}  type="text"/>
                        <button onClick={this.ClickFun}>提示输入数据</button>
                        <input ref={this.myRef2} onBlur={this.BlurFun}  type="text"/>
                     </div>
                 )
             }

             ClickFun = ()=>{
                 alert(this.myRef.current.value);
             }

             BlurFun = ()=>{
                alert(this.myRef2.current.value);
             }
         }
         //渲染组件到页面
         ReactDOM.render(<Demo/>,document.getElementById("app"))
</script>

五、React中的事件处理

虽然之前咱们可以使用ref来标识想要的节点对象,但是官网提示【勿过度使用 Refs】,可以通过event.target得到发生事件的DOM元素对象,一般用于当发生事件的元素恰好是你要操作的元素就可以避免使用ref。

总结:

  1. 通过onXxx属性指定事件处理函数(注意大小写)

    1. React使用的是自定义(合成)事件, 而不是使用的原生DOM事件

    2. React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)

  2. 通过event.target得到发生事件的DOM元素对象

六、非受控组件与受控组件

  • 非受控组件

表单项不与state数据相向关联, 需要手动读取表单元素的值

借助于 ref获取真实DOM,在通过value获得输入值,使用原生 DOM 方式来获取表单元素值

非受控组件: 表单项不与 state 数据相向关联, 需要手动读取表单元素的值

  • 受控组件

组件中的表单项根据state状态数据动态初始显示和更新显示, 当用户输入时实时同步到状态数据中

也就是实现了页面表单项与 state 数据的双向绑定

实现方式

  1. 在 state 中添加一个状态,作为表单元素的value值(控制表单元素值的来源)
  2. 给表单元素绑定 change 事件,将 表单元素的值 设置为 state 的值(控制表单元素值的变化)
{/* react中虚拟DOM中的name的值一定是和state属性相关的值 */}
<p>用户名:<input type="text" name='user' value={user} onChange={this.changeForm} /></p>
{/* a是咱们自定义的属性名,后期获取的时候需要使用getAttribute()方法 */}
<p>用户名:<input type="text" a='user' value={user} onChange={this.changeForm} /></p>
{/* 在DOM元素对象身上还可以通过data-a的形式添加,后期获取的时候使用dataset.a */}
<p>用户名:<input type="text" data-a='user' value={user} onChange={this.changeForm} /></p>

changeForm = (e) => {
    this.setState({
    	[e.target.name]: e.target.value
    	[e.target.getAttribute('a')]: e.target.value
        [e.target.dataset.a]: e.target.value
    })
}

七、高阶函数

本身也是一个函数

特点:接收函数类型的参数或者返回一个新函数

function fn(f){
	return ()=>{
	    
	}
}


fn1(()=>{})



const p = new Promise((resolve,reject)=>{});
then(value=>{},reason=>{})
p.catch(reason=>{})
setTimeout(()=>{})  setInterval(()=>{})
let arr = [1,2,3];
arr.foreach(()=>{})
arr.map((item,index)=>{})

函数类型参数:Promise、then、catch、setTimeout、setInterval、forEach、map、filter、every、some等

返回函数:闭包函数、bind

function fun(a, b) {

function fun1() {

​ a++;

​ return a;

}

return fun1;

}

fun(10,20);


<p>用户名:<input type="text"  value={user} onChange={this.saveData('user')} /></p>
       
saveData = (type) => {
    return (e) => {
        this.setState({
            [type]: e.target.value
        })
    }
}


八、React脚手架

1、 脚手架的安装与启动

  • 全局安装
  npm i -g create-react-app
  • 切换目录
  create-react-app 目录名称
  • 进入目录
  cd 目录名称
  • 启动项目
  npm start

2、脚手架目录说明

  • public 脚手架服务的网站根目录(静态资源目录)
    • index.html :webpack打包html的模板文件
  • src 源代码开发的目录
    • index.js 打包的入口文件
  • .gitignore git的配置忽略文件

3、 props 校验 (了解)

允许在创建组件的时候,就指定 props 的类型、格式

作用:捕获使用组件时因为props导致的错误,给出明确的错误提示,增加组件的健壮性

实现方式:

  1. 导入 prop-types 包 (脚手架自带,无需额外安装)
  2. 使用propTypes来给组件的props添加校验规则

4、 props 设置类型和设置默认值

作用:给 props 设置默认值,在未传入 props 时生效

在class类中实现方式:

import React, { Component } from 'react'
//1、导包
import types from 'prop-types'
export default class Stu extends Component {
    //2、在类内使用静态属性 propTypes 来约束 
    //注意:这个属性不能修改
    static propTypes = {
        name: types.string.isRequired,
        age: types.number,
        hobby: types.array
    }

    //3、为传入的属性设置默认值
    static defaultProps = {
        name: '李四',
        age: 24
    }

    render() {
        let { name, age, hobby } = this.props;
        return (
            <div>
                <p>姓名:{name}</p>
                <p>年龄:{age}</p>
                <p>爱好:{hobby.join(',')}</p>
            </div>
        )
    }
}

5、函数式组件中使用props校验

App.jsx

import Stu from './Stu'
export default function App() {
    //声明一个对象
    let info = {
        // name: '张三',
        age: 23,
        hobby: ['吃饭', '睡觉', '打豆豆']
    }
    return (
        <div>
            <Stu {...info} />
        </div>
    )
}

Stu.jsx

//1、导包
import PropTypes from 'prop-types'
//2、为Stu设置propTypes属性
Stu.propTypes = {
    name: PropTypes.string.isRequired,
    age: PropTypes.number,
    hobby: PropTypes.array
}
Stu.defaultProps = {
    name: '王二麻子'
}
export default function Stu(props) {
    let { name, age, hobby } = props;
    return (
        <div>
            <p>姓名:{name}</p>
            <p>年龄:{age}</p>
            <p>爱好:{hobby.join('-')}</p>
        </div>
    )
}

九、React组件的生命周期

组件对象从创建到死亡所经历的特定的阶段

React组件对象包含一系列钩子函数(生命周期回调函数),在特定的时刻调用

我们在定义组件时,在特定的生命周期回调函数中做特定的工作

全称:生命周期函数或者是生命周期钩子,其中主要研究的就是组件生命周期的三个阶段

概念:生命周期函数指在某一时刻组件会自动调用执行的函数

本质就是组件内的一些方法,当然在这里咱们讨论的是类式组件,因为函数式组件中有相应的Hook函数

特点就是能够自动执行

其中包括ComponentDidMount()ComponentDidUpdate()ComponentWillUnmount()

1.1 挂载阶段

流程: constructor ==> render ==> componentDidMount

子父类组件父组件渲染:父构造->父render->子构造->子render->子完成挂载->父完成挂载

父constructor =>父render=>子constructor =>子render=>子componentDidMount===>父componentDidMount

触发: ReactDOM.render(): 渲染组件元素

  • constructor: 创建组件时,最先执行

    一般用于:

      1. 初始化state
      1. 为事件处理程序绑定this

        this.xx = this.xx.bind(this)

  • render: 每次组件渲染都会触发

    • 注意: 不能在render中调用setState()
  • componentDidMount: 组件挂载(完成DOM)渲染后

    注意: 这个生命周期钩子从虚拟DOM转化成真实DOM之后,挂载在root元素之后,只执行这么一次

    除非这个组件被卸载之后,重新挂载,还会在此执行,且还是一次

    一般用于:

      1. 定时器
      2. 发送网络请求 axios
      3. 订阅频道
      1. DOM操作 (只要是组件一上来就要做的事情,都应该写在挂载成功的回调中)

1.2 更新阶段

componentDidUpdate: 组件更新(完成DOM渲染)后

流程: render ==> componentDidUpdate

触发: setState() , 组件接收到新的props

如果是父组件中嵌套子组件,
父组件中的状态被更新了:
父组件render->子组件render->子组件componentDidUpdate->…->父组件componentDidUpdate
子组件中的状态被更新了:那么只有这个子组件中的render->这个子组件中的componentDidUpdate执行

1.3 卸载阶段

componentWillUnmount: 组件卸载(从页面中消失) 执行清理操作

测试:当我们更改组件内容并保存之后,React会将之前的组件卸载并重新挂载一个新的组件

顺序:自己的构造->自己的render->卸载钩子函数->重新挂载

constructor->rende->componentWillUnmount->componentDidMount

父组件内容变化时触发
在这里插入图片描述

子组件内容变化触发:

在这里插入图片描述

例如:

  • 清空定时器
  • 取消订阅

流程: componentWillUnmount

触发: 不再渲染组件

十、Hook

  • Hook 是 React 16.8 之后的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性
  • Hook 也叫钩子,本质就是函数,能让你在函数式组件中使用状态生命周期函数等功能
  • Hook 语法 基本已经代替了类组件的语法,后面的 React 项目就完全是用 Hook 语法了

5.1 useState

用来定义状态数据,可以多次调用, 产生多个状态数据

mport React, { useState } from 'react'

export default function App() {
    // console.log(React.useState);
    /**
     * React.useState()方法返回一个数组,
     * 数组中的第一个元素记录state中的状态数据
     * 数组中的第二个元素是一个方法,用来修改状态数据,类似于setState
     */

    //对象解构赋值            
    //注意:React.useState()方法可以在导入react的时候直接做解构,后面就可以直接使用useState()方法
    let [isLogin, setLogin] = useState(true);

    function changeLogin() {
        setLogin(!isLogin);
    }

    return (
        <div>
            <p>现在的状态:{isLogin ? '已登录' : "未登录"}</p>
            {/* 按钮中直接调用函数,不需要写this */}
            <button onClick={changeLogin}>修改状态</button>
        </div >
    )
}

5.2 useRef

功能和类式组件中的createRef()方法相似,可以帮助我们在函数式组件中方便获取真实的DOM元素对象。

import React, { useRef } from 'react'

export default function App() {
    let ipt = useRef();
    let box = useRef();
    let show = () => {
        let v = ipt.current.value;
        box.current.innerHTML += '用户名:' + v + '<br/>';
    }
    return (
        <div>
            <p>
                用户名:<input type="text" ref={ipt} />&nbsp;
                <button onClick={show}>获取</button>
            </p>
            <hr />
            <div id="result" ref={box} style={{ width: 500, height: 500, border: '1px dashed #ccc' }}></div>
        </div>
    )
}

5.3 useEffect

是一个高阶函数,可以在一个组件中多次使用,相当于componentDidMount(组件挂载完成),

componentDidUpdate(组件更新完成) 和 componentWillUnmount(组件将要卸载之前)的组合

语法:React.useEffect(()=>{},[])

当如果不希望在组件更新时再次执行,只希望在组件挂载时执行,则需要给useEffect方法设置第二个参数为数组

数组参数的含义:设置了哪些状态数据修改之后,才会执行回调,

分为两种情况:

  • 数组的参数如果没有任何值的话,表示什么状态数据都不更新,回调函数不执行
  • 当数组中传入state的状态数据,表示当这个状态数据被更新了的时候,才执行回调函数
import React, { useEffect, useState } from 'react'

export default function App() {
    //声明状态
    let [count, setCount] = useState(0);
    let [sex, setSex] = useState(true);
    //调用
    //第一个参数为函数,箭头函数或匿名函数均可
    //这个函数的作用等效于ComponentDidMount和ComponentDidUpdate
    //这个方法可以执行多次
    useEffect(function () {
        console.log('我既执行ComponentDidMount效果,也执行ComponentDidUpdate效果-1');
    });

    useEffect(function () {
        console.log('我既执行ComponentDidMount效果,也执行ComponentDidUpdate效果-2');
    });

    //当如果只想要ComponentDidMount效果,则需要给useEffect方法传入第二个参数,为数组
    //数组的参数如果没有任何元素的话,表示不更新状态

    /* useEffect(() => {
        console.log('我只执行ComponentDidMount效果');
    }, []) */

    //如果想要某一个状态数据改变的时候,需要执行,则将这个状态数据添加即可
    /* useEffect(() => {
        console.log('我只执行ComponentDidMount效果');
    }, [sex]) */

    //如果想要模拟ComponentwillUnmount
    //在脚手架中当尝试修改组件之后,react会将组件先卸载,然后再来一个新的重新挂载
    useEffect(() => {
        return () => {
            console.log('组件将要被卸载时...')
        }
    })

    let changeAdd = (num) => {
        return () => {
            count = count + num;
            setCount(count);
        }
    }
    let changeSex = () => {
        setSex(!sex);
    }
    return (
        <div>
            <ul>
                <li>
                    <p>数值:{count}</p>
                    <button onClick={changeAdd(10)}>新增</button>
                </li>
            </ul>
            <ul>
                <li>
                    <p>性别:{sex ? '男' : '女'}</p>
                    <button onClick={changeSex}>修改</button>
                </li>
            </ul>
        </div>
    )
}

但是使用useEffect()方法的时候有一个很重要的细节,那就是useEffect方法的第一个函数不能是一个异步函数

也就是说,不能出现async,原因是如果设置了async,返回值就成了promise对象,非一个函数了。

如果想要在useEffect()方法中添加异步代码,需要在函数中在单独声明一个函数

//如果想要模拟ComponentwillUnmount
//在脚手架中当尝试修改组件之后,react会将组件先卸载,然后再来一个新的重新挂载
useEffect(() => {
    return () => {
        async function main() {
            console.log('在这里可以执行异步代码逻辑...');
        }
        main();
    }
})

使用Hook 注意:

  1. 只在最顶层使用 Hook,不要在条件或循环中

  2. 只在React组件函数内部中调用 Hook, 不要在组件函数外部调用

十一、脚手架配置代理(proxy)的方式

CORS:

请求url:http://www.baidu.com
发送url:http://www.jd.com
response.setHeader('Access-Control-Allow-Origin','*');

在这里插入图片描述

  • 通过express快速搭建一个服务

  • 创建一个图书组件

import React, { useEffect } from 'react'
import axios from 'axios'
export default function Books() {
	//在组件挂载完成之后,相当于ComponentDidMount()方法,进行ajax请求
    useEffect(() => {
        //ajax请求
        async function main() {
            let result = await axios.get('http://127.0.0.1:5000/books');
            console.log(result)
        }
        main();
    }, [])
    return (
        <div>Books</div>
    )
}
  • 在package.json文件中添加一行配置
"proxy":"http://127.0.0.1:5000"
  • 重启服务 这一步非常重要
  • 修改请求的url地址
async function main() {
    let result = await axios.get('/books');
    console.log(result)
}
main();
  • 但是这种方式相对单一,只能配置一个代理,如果想要配置多个代理,这就不行了
  • 解决办法:需要在src的根目录中创建一个setupProxy.js文件,注意文件名不能修改
    • 安装一个包:npm i http-proxy-middleware
//1、导包
const proxy = require('http-proxy-middleware');
//2、导出一个方法
module.exports = function (app) {
    app.use(
        proxy.createProxyMiddleware('/api', {
            //请求的目的地址
            target: 'http://127.0.0.1:5000',
            //控制服务器收到的请求头中Host的值
            changeOrigin: true,
            //重写路径
            pathRewrite: {
                '^/api': ''
            }
        })
    )
}
  • 修改代码
import React, { useEffect, useState } from 'react'
import axios from 'axios'
export default function Books() {
    let [booklist, setBookList] = useState([]);
    useEffect(() => {
        //ajax请求
        async function main() {
            let result = await axios.get('/api/books');
            if (result.status >= 200 && result.status < 300) {
                setBookList(result.data)
            }

        }
        main();
    }, [])
    return (
        <div>
            <h3>图书列表</h3>
            <hr />
            <ul>
                {
                    booklist.map(item => {
                        return <li key={item.id}>{item.name}</li>
                    })
                }
            </ul>
        </div>
    )
}
  • 再次基础之上添加模糊搜索功能
  • Book.jsx
import React, { useState } from 'react'
import Header from './Header/Header'
import Main from './Main/Main'
import axios from 'axios'
export default function Books() {
    let [booklist, setBookList] = useState([]);
    let searchBookList = async (v) => {
        let result = await axios.get('/api/books', {
            params: {
                name: v
            }
        });
       setBookList(result.data)
    }
    return (
        <div>
            <h3>图书列表</h3>
            <hr />
            <Header searchBookList={searchBookList} />
            <Main booklist={booklist} />
        </div>
    )
}

Header.jsx

import React, { useRef } from 'react'

export default function Header(props) {
    let { searchBookList } = props;
    let bookname = useRef();
    let searchBook = () => {
        //获取文本框中的数据
        let book = bookname.current.value;
        if (book === '') {
            alert('请输入书名');
            return;
        }
        searchBookList(book)
        //清空文本框中的数据
        bookname.current.value = ''
    }

    return (
        <div>
            请输入图书名称:<input type="text" ref={bookname} /> &nbsp;
            <button onClick={searchBook}>搜索</button>
        </div>
    )
}

Main.jsx

import React from 'react'

export default function Main(props) {
    let { booklist } = props;
    return (
        <ul>
            {
                booklist.map(item => {
                    return <li key={item.id}>{item.name}</li>
                })
            }

        </ul>
    )
}

十二、组件间的通信

react中的组件间的通信有三种:父子间、兄弟间、祖孙间

2.1 父子间组件通讯(props)

2.1.1 父->子

直接通过props属性向子组件传递数据,可以是一般的纯数据 <子组件 num={20}/>

也可以是父组件中的状态数据,当父组件中的状态数据更改,也会影响到子组件

2.1.2 子->父

默认情况下子是被动接收数据的一方,是没有办法直接修改父组件中的数据,但是如果父组件传递一个可以修改父

组件的状态数据的方法,通过props的形式给子组件,那么子组件修改的值是可以 影响父组件中的状态数据

2.2 祖孙间(任意后代)组件通讯(context)

在这里插入图片描述

2.2.1 祖-> 孙

  • 调用 React. createContext() 创建 context 对象 【谁主动传递数据,就在谁的身上添加这个对象】
const context = React.createContext() 
  • 外部组件中使用 context 上的 Provider 组件作为父节点, 使用value属性定义要传递的值
<context.Provider value={要传递的值}>  提供数据
  <div className="App"> 
    	<Child1 /> 
  </div> 
</context.Provider>
  • 孙组件中导入祖组件
import { context } from '../GrandFather/GrandFather'
  • 孙组件接收祖组件传递的数据
  • 方式1:利用Hook钩子函数中的useContext方法接收传递的数据
import React, { useContext } from 'react'
let name = useContext(context);
console.log(name);
  • 方式2:利用context 上的 Consumer组件作为父节点,在其中使用{}添加回调函数写法
import React from 'react'
import { context } from '../GrandFather/GrandFather'
export default function Son() {
    return (
        <div style={{ width: 400, height: 200, border: '1px dashed green' }}>
            <context.Consumer>
                {
                    name => <p>姓名:{name}</p>  #name可以自定义,表示提供者提供的value数据
                }
            </context.Consumer>
        </div>
    )
}

2.2.2 孙->祖

  • 将祖组件中的数据做成状态数据,然后将初始状态数据以及修改状态数据通过value一并传递给孙组件
  • 孙组件接收并进行修改

例如:将GrandFather组件中的数据传递给Son组件

在这里插入图片描述

GrandFather.jsx

import React, { createContext, useState } from 'react'
import Father from '../Father/Father'
export let context = createContext();
export default function GrandFather() {
    // let name = '李四'
    let [name, setName] = useState('李四');
    let [age, setAge] = useState(20)
    return (
        <div style={{ width: 400, height: 400, border: '1px dashed #ccc' }}>
            <h4>GrandFather组件</h4>
            <p>姓名:{name}</p>
            <p>年龄:{age}</p>
            <hr />
            <context.Provider value={[name, setName, age, setAge]}>
                <Father />
            </context.Provider>
        </div>
    )
}

Father.jsx

import React, { useContext } from 'react'
import Son from '../Son/Son'
import { context } from '../GrandFather/GrandFather'
export default function Father(props) {
    // let { age, changeAge } = props;
    let result = useContext(context);
    console.log(result)
    let updateAge = (num) => {
        return () => {
            // changeAge(20)
            result[3](result[2] + num);
        }
    }
    return (
        <div style={{ width: 400, height: 300, border: '1px dashed red' }}>
            <h4>Father组件</h4>
            <span>年龄:{result[2]}</span>
            <hr />
            <button onClick={updateAge(100)}>更新年龄</button>
            <hr />
            <Son />
        </div>
    )
}

Son.jsx

// import React, { useContext } from 'react'
import { context } from '../GrandFather/GrandFather'
export default function Son() {
    // let name = useContext(context);
    let changeName = (f) => {
        f('王五');
    }
    return (
        <context.Consumer>
            {
                value => (
                    <div style={{ width: 400, height: 200, border: '1px dashed green' }}>
                        <h4>Son组件</h4>
                        <p>姓名:{value[0]}</p>
                        <button onClick={e => changeName(value[1])}>修改姓名</button>
                    </div >
                )
            }
        </context.Consumer>
    )
}

一般应用中不使用, 但一些插件库内部会使用context封装, 如: react-redux

2.3 兄弟间组件通讯(pub/sub)

注意:兄弟间的组件是非嵌套式写法的组件

在这里插入图片描述

发布订阅机制: publish(发布者) / subscribe(接受者)

pubsub-js是一个用JS编写的库。

利用订阅发布模式, 当一个组件的状态发生了变化,可以通知其他任意组件更新这些变化

  • 安装:
npm install pubsub-js
  • 导入
import PubSub from "pubsub-js" // 导入的PubSub是一个对象.提供了发布/订阅的功能
  • 发送消息的组件调用
PubSub.publish('频道名称','值');
  • 接收消息的组件中调用
PubSub.subscribe('频道名称',(msg,data)=>{
	 //回调函数中的第一个参数:频道名称
     //回调函数中的第二个参数:值
     console.log(msg);
     console.log(data);
});
  • 取消订阅
PubSub.unsubscribe('频道名称');

A.jsx

import React from 'react'
import B from '../B/B'
import C from '../C/C'

export default function A() {
    return (
        <div>
            <B />
            <C />
        </div>
    )
}

B.jsx

import React from 'react'
import PubSub from 'pubsub-js'
export default function B() {
    let sendData = () => {
        PubSub.publish('keywords', '张三丰');
    }
    return (
        <div style={{ width: 400, height: 100, border: '1px dashed #ccc' }}>
            <h4>我是B组件</h4>
            <button onClick={sendData}>C组件发送数据</button>
        </div>
    )
}

C.jsx

import React, { useEffect, useState } from 'react'
import PubSub from 'pubsub-js'
export default function C() {
    let [name, SetName] = useState('')
    useEffect(() => {
        PubSub.subscribe('keywords', (msg, data) => {
            SetName(data);
        });
        return () => {
            PubSub.unsubscribe('keywords');
        }
    }, [])

    return (
        <div style={{ width: 400, height: 150, border: '1px dashed red' }}>
            <h3>我是C组件</h3>
            <p>姓名:{name}</p>
        </div>
    )
}

十三、react路由版本6

1.基本使用

  • 安装 npm i react-router-dom

1.1.引入BrowserRouter

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

1.2.路由中routes route Link NaLink的使用

//或者使用Link  NavLink 会带激活的样式
<NavLink to="/books">图书</NavLink>
<NavLink to="/news">新闻列表</NavLink>
//使用Routes 包裹route路由
<Routes>
    <Route path='/about' element={<About />} >
    	//二级路由写在一级路由标签中
        <Route path='/about/news' element={<News />} />
        <Route path='/about/message' element={<Message />} />
    </Route>
	<Route path='/home' element={<Home />} >
    	<Route path='/home/news' element={<News />} />
        <Route path='/home/message' element={<Message />} />
    </Route>
</Routes>

1.3.Outlet 标签

  • 在Home和About中对应需要显示二级路由的位置添加一个组件标签<Outlet/>

作用:当<Route>产生嵌套时,渲染匹配的子路由组件。

1.4.caseSensitive路由中的大小写敏感

<Route caseSensitive path='/about' element={<About />} ></Route>

1.5.path='*'路由的统配符匹配

当在地址栏中出现不存在的路由规则进行匹配时,页面中没有任何的相关提示,这显然不太友好

我们可以单独为这样的匹配创建一个组件

<Route path='*' element={<NotFound />} />

NotFound.jsx

import React from 'react'

export default function NotFound() {
    return (
        <div>NotFound</div>
    )
}

1.6.Navigate显示默认路由(可以理解为重定向)

  • 在Routes中在添加一个路由规则
import { Navigate } from 'react-router-dom'
<Route index element={<Navigate />} />

这里的index是简写形式,如果写全是这样的 index={true}

index本身有默认名称的含义,这句话意思是当访问哪一个路由时,默认显示哪一个二级路由

注意:这句话一定要写在一级路由中

<Route path='/home' element={<Home />} >
    <Route path='/home/news' element={<News />} />
    <Route path='/home/message' element={<Message />} />
    <Route index element={<Navigate to="/home/news" />} />
</Route>

2. 路由组件中的内置Hooks

2.1 useRoutes()

本质:是一个函数

作用:根据设置的路由表(也就是给这个函数中设置数组参数)、可以动态的创建<Routes><Route>

使用:

  • 导入
import {  useRoutes } from 'react-router-dom'
  • 配置路由表
let routeArr = useRoutes([
        {
            path: '/home',
            element: <Home />,
            children: [
                {
                    path: 'news',
                    element: <News />
                },
                {
                    path: 'message',
                    element: <Message />
                },
                {
                    index: true,
                    element: <Navigate to='news' />
                }
            ]
        },
        {
            path: '/about',
            element: <About />,
            children: [
                {
                    path: 'news',
                    element: <News />
                },
                {
                    path: 'message',
                    element: <Message />
                },
                {
                    index: true,
                    element: <Navigate to='news' />
                }
            ]
        },
        {
            path: '*',
            element: <NotFound />
        },
        {
            path: '/',
            element: <Index />
        }
    ])
  • 在需要使用路由的位置调用
<div className="col-xs-6">
       {routeArr}
</div>
  • 考虑到后期可以单独的使用这个路由,咱们可以在src中创建一个routes目录,添加一个route.js文件
import { Navigate } from 'react-router-dom'
import About from '../pages/About/About';
import Home from '../pages/Home/Home';
import News from '../pages/News/News';
import Message from '../pages/Message/Message';
import NotFound from '../pages/NotFound/NotFound';
import Index from '../pages/Index/Index';
export default [
    {
        path: '/home',
        element: <Home />,
        children: [
            {
                path: '/home/news',
                element: <News />
            },
            {
                path: '/home/message',
                element: <Message />
            },
            {
                index: true,
                element: <Navigate to='news' />
            }
        ]
    },
    {
        path: '/about',
        element: <About />,
        children: [
            {
                path: '/about/news',
                element: <News />
            },
            {
                path: '/about/message',
                element: <Message />
            },
            {
                index: true,
                element: <Navigate to='news' />
            }
        ]
    },
    {
        path: '*',
        element: <NotFound />
    },
    {
        path: '/',
        element: <Index />
    }
]
  • 在APP组件中导入并使用
import routess from './routes/route'
let routeArr = useRoutes(routess)

2.2 useParams()获取params数据

  • 作用:返回当前匹配路由的params参数的对象

以图书案例为例

APP.jsx

import React from 'react'
import Books from './Component/Books/Books'
import { useRoutes } from 'react-router-dom'
import BookDetails from './Component/BookDetails/BookDetails'
export default function App() {
    let routeArr = useRoutes([
        {
            path: '/',
            element: <Books />
        },
        {
            path: '/book/:id',
            element: <BookDetails />
        }
    ]);
    return (
        <div>
            {routeArr}
        </div>
    )
}

Main.jsx

import React from 'react'
import { Link } from 'react-router-dom'
export default function Main(props) {
    let { booklist } = props;
    return (
        <ul>
            {
                booklist.map(item => {
                    return <Link to={`/book/${item.id}`} key={item.id}><li>{item.name}</li></Link>
                })
            }
        </ul>
    )
}

BookDetails.jsx

import React, { useState } from 'react'
import { useParams } from 'react-router-dom'
import { useEffect } from 'react';
import axios from 'axios';
import './BookDetails.css'
export default function BookDetails() {
    let [bookOne, setBookOne] = useState({});
    //接收路由中的参数
    let { id } = useParams();
    useEffect(() => {
        async function main() {
            let { data } = await axios.get(`/api/books/${id}`);
            setBookOne(data)
        }
        main();

    }, [])
    return (
        <div>
            <h1>{bookOne.name}</h1>
            <hr />
            <span className='autor'>作者:{bookOne.autor}</span>
            <p>{bookOne.infro}</p>
        </div>
    )
}

BookDetails.css

h1 {
    text-align: center;
    font-size: 40px;
}

.autor {
    width: 800px;
    height: 30px;
    margin: 20px auto;
    display: block;
    line-height: 30px;
}

p {
    width: 800px;
    margin: 20px auto;
    line-height: 30px;
    color: gray;
}

img {
    display: block;
    margin: 20px auto;
    width: 540px;
    height: 760px;
}

2.3 useSearchParams()获取Search数据

  • 作用:用于读取和修改当前位置的 URL 中的查询字符串

APP.jsx

import React from 'react'
import Books from './Component/Books/Books'
import { useRoutes } from 'react-router-dom'
import BookDetails from './Component/BookDetails/BookDetails'
export default function App() {
    let routeArr = useRoutes([
        {
            path: '/',
            element: <Books />
        },
        {
            path: '/book',
            element: <BookDetails />
        },
    ]);
    return (
        <div>
            {routeArr}
        </div>
    )
}

Main.jsx

import React from 'react'
import { Link } from 'react-router-dom'
export default function Main(props) {
    let { booklist } = props;
    return (
        <ul>
            {
                booklist.map(item => {
                    return <Link to={`/book?id=${item.id}`} key={item.id}><li>{item.name}</li></Link>
                })
            }
        </ul>
    )
}

BookDetails.jsx

import React, { useState } from 'react'
import { useSearchParams } from 'react-router-dom'
import { useEffect } from 'react';
import axios from 'axios';
import './BookDetails.css'
export default function BookDetails() {
    let [bookOne, setBookOne] = useState([]);
    let [search] = useSearchParams();
    let id = search.get('id');
    ....
}

2.4 useLocation()获取state数据

作用:获取当前 location 信息,功能类似于JS中的location对象

APP.jsx

import React from 'react'
import Books from './Component/Books/Books'
import { useRoutes } from 'react-router-dom'
import BookDetails from './Component/BookDetails/BookDetails'
export default function App() {
    let routeArr = useRoutes([
        {
            path: '/',
            element: <Books />
        },
        {
            path: '/book',
            element: <BookDetails />
        },
    ]);
    return (
        <div>
            {routeArr}
        </div>
    )
}

Main.jsx

import React from 'react'
import { Link } from 'react-router-dom'
export default function Main(props) {
    let { booklist } = props;
    return (
        <ul>
            {
                booklist.map(item => {
                    return <Link to={`/book`} state={{ id: item.id }} key={item.id}><li>{item.name}</li></Link>
                })
            }
        </ul>
    )
}

BookDetails.jsx

import { useLocation } from 'react-router-dom'
let { state: { id } } = useLocation();
console.log(id)

2.5 useNavigate()编程式导航

作用:返回一个函数用来实现编程式导航。 可以更改页面的 URL,可以替代组件

  • 导入
import { useNavigate } from 'react-router-dom'
  • 调用
let navigate = useNavigate();
  • 使用
<li key={item.id} onClick={() => {
    navigate(`/book/${item.id}`)
    //navigate(`/book/?id=${item.id}`)
    //navigate(`/book,{state:{id:item.id}})
}}>{item.name}</li>
  • 接收
let { id } = useParams();
console.log(id)

Main.jsx

import React from 'react'
import { useNavigate } from 'react-router-dom'
export default function Main(props) {
    let { booklist } = props;
    //调用useNavigate方法
    let navigate = useNavigate();
    return (
        <ul>
            {
                booklist.map(item => {
                    return <li key={item.id} onClick={() => {
                        navigate(`/book/${item.id}`)
                    }}>{item.name}</li>
                })
            }
        </ul>
    )
}

APP.jsx

import React from 'react'
import Books from './Component/Books/Books'
import { useRoutes } from 'react-router-dom'
import BookDetails from './Component/BookDetails/BookDetails'
export default function App() {
    let routeArr = useRoutes([
        {
            path: '/',
            element: <Books />
        },
        {
            path: '/book/:id',
            element: <BookDetails />
        },
    ]);
    return (
        <div>
            {routeArr}
        </div>
    )
}

BookDetails.jsx

import React, { useState,useEffect } from 'react'
import { useParams } from 'react-router-dom'
import axios from 'axios';
import './BookDetails.css'
export default function BookDetails() {
    let [bookOne, setBookOne] = useState([]);
    let { id } = useParams();
    useEffect(() => {
        async function main() {
            let { data } = await axios.get(`/api/bookOne`, {
                params: { id }
            });
            setBookOne(data)
        }
        main();
    }, [])
    return (
        <div>
            <h1>{bookOne.name}</h1>
            <hr />
            <span className='autor'>作者:{bookOne.autor}</span>
            <p>{bookOne.infro}</p>
            <img src={'/image/' + bookOne.img} alt={bookOne.name} />

        </div>
    )
}

如果想要使用类似于请求字符串的写法,咱们还可以这样,例如:/book?id=1

Main.jsx

{
    booklist.map(item => {
        return <li key={item.id} onClick={() => {
            navigate(`/book?id=${item.id}`)
        }}>{item.name}</li>
	})
}

APP.jsx

let routeArr = useRoutes([
    {
        path: '/',
        element: <Books />
    },
        {
        path: '/book',
        element: <BookDetails />
    },
]);

BookDetails.jsx

import { useSearchParams } from 'react-router-dom'
let [search] = useSearchParams();
let id = search.get('id');
console.log(id)

如果想要使用state方式传参,咱们还可以这样做

Main.jsx

{
    booklist.map(item => {
        return <li key={item.id} onClick={() => {
            navigate('/book', {
                state: {
                    id: item.id
                }
            })
        }}>{item.name}</li>
})
}

APP.jsx

let routeArr = useRoutes([
    {
        path: '/',
        element: <Books />
    },
        {
        path: '/book',
        element: <BookDetails />
    },
]);

BookDetails.jsx

import { useLocation } from 'react-router-dom'
let result = useLocation();
let { id } = result.state;
console.log(id)

注:Link与navigate的选择问题

  • Link更适合于简单的路径变化,而且也是一种无逻辑的组件
  • navigate用于复杂的场景,进行变化

3. useNavigate路由后退与replace替换模式

useNavigate()方法中可以实现路由后退以及replace的替换模式

3.1 路由后退

  • 导入
import {  useNavigate } from 'react-router-dom'
  • 调用
let navigate = useNavigate();
  • 在界面中添加一个后退按钮
<div style={{ margin: 10 }}><a className="btn btn-primary" onClick={back}>后退</a></div>
let back = () => {
    	//功能类似于原生JS中的history.go(-1)
    	//后退
        navigate(-1);
    	//前进
    	//navigate(1); //用的较少
}

3.2 replace替换模式

Main.jsx中的在跳转URL中添加replace

{
    booklist.map(item => {
        return <li key={item.id} onClick={() => {
            navigate('/book', {
                //路径替换
                replace: true,
                state: {
                    id: item.id
                }
            })
        }}>{item.name}</li>
})
}

4. 路由组件的懒加载(lazyload)

问题:
所有路由组件代码是打包在一块的, 打开首页就会加载, 但我们开始只需要看到首页路由的效果,
也就是只需要执行首页路由组件代码

解决:
对路由组件进行懒加载处理

深入理解
对路由组件进行拆分/单独打包 => import函数动态引入
访问路由时才去后台加载对应的打包文件 => lazy函数
指定loading界面 =>

import {lazy, Suspense} from 'react'

// 懒加载动态引入组件
const About = lazy(() => import('../pages/About'))

// 路由表
{
  element: <Suspense fallback={<div>正在加载中...</div>}>
    <About />
  </Suspense>
}

在这里插入图片描述

十四、redux

一.@reduxjs/toolkit使用

1.1安装

  • 安装:
npm install @reduxjs/toolkit

在src目录中的store目录中的index.js中添加

1.2导入包中的对象

  • 导入包中的对象
import { createSlice, configureStore } from '@reduxjs/toolkit'

1.3创建切片对象

  • 创建切片对象
//这个方法需要传入一个参数是对象,在这个对象中必须要有的是name属性
const counter = createSlice({
    //这个名称会作为action.type中的前缀,避免命名冲突,自动追加
    name: 'count',
    //初始状态数据
    initialState: {
        num: 10
    },
    //定义reducers
    //定义reducer更新状态数据的函数
    //而里面的方法在后期组件中执行dispatch时是作为action函数的函数名去使用的
    reducers: {
        //需要传入两个参数,一个是state(代理的proxy对象)、一个是action
        add(state, { payload }) {
            //输出
            // console.log(state);
            // console.log(action)
            state.num += payload;
        },
        dec(state, { payload }) {
            //输出
            // console.log(state);
            // console.log(action)
            state.num -= payload;
        }
    }
});

1.4获取action函数并导出方法

  • 获取action函数并导出方法
export let { add,dec } = counter.actions;

1.5创建store对象

  • 创建store对象
//调用rtk的configureStore方法,
//该方法相当于集成了redux和redux-redux的createStore、combineReducers、middleware以及默认支持了Redux DevTools。
const store = configureStore({
    reducer: counter.reducer
})

1.6异步使用redux

  • 异步使用redux
    在store/index.js文件中只需要单独声明一个方法并导出,不需要其他操作
//单独创建一个函数
export let asynctianjia = (payload) => {
    return dispatch => {
        //执行异步操作
        setTimeout(() => {
            dispatch(add(payload));
        }, 1000)
    }
}

1.7导出store

  • 导出
export default store;

1.8获取状态数据

  • 获取状态数据
console.log(store.getState())

1.9修改状态数据

  • 修改状态数据
store.dispatch(add(10))
store.dispatch(dec(5))

1.10store.subscribe()来重新渲染数据

  • store.subscribe()来重新渲染数据
    不要忘记在主入口中重新渲染组件,因为redux只负责状态管理,不负责组件的重新渲染
import store from './store';
store.subscribe(() => {
  root.render(
    <>
      <App />
    </>
  );
})

二、react-redux 使用

redux的问题:

  • 需要自己绑定监听来重新渲染整个组件

  • 每次state更新, 都需要重新render所有组件(使用subscribe方法进行订阅redux状态的更新) ,

  • 效率低,应该有用到state数据的组件需要render

解决: 使用 react-redux 库,想要使用必须下载npm i react-redux

文档: https://react-redux.js.org/

react-redux 是一个 react 插件,用来简化 react 中使用 redux

在这里插入图片描述

Provider 组件:用来包裹整个 React 应用,接收 store 属性,为应用提供 state 和 更新 state 的函数。

核心Hook函数

  • useSelector 函数: 读取state数据
  • useDispatch 函数: 得到分发action的dispatch函数

2.1 使用步骤

  • 下载
  npm i react-redux
  • 通过Provider向App及其所有后代组件提供store
  import React from 'react';
  import ReactDOM from 'react-dom/client';
  import App from './App';
  import { Provider } from 'react-redux'
  import store from './store';
  const root = ReactDOM.createRoot(document.getElementById('root'));
  root.render(
    <>
      <Provider store={store}>
        <App />
      </Provider>
    </>
  );
  • 修改Redux.jsx组件中的代码,进行调用Hook函数
  import React from 'react'
  import { add, dec, asynctianjia } from '../../store/modules/counter';
  import { add as a, dec as b } from '../../store/modules/Hit'
  import { useSelector, useDispatch } from 'react-redux';
  //调用useSelector方法来获取redux中所有的状态数据,redux会将所获取到的状态通过state参数获取(参数名可以自定义)
  let { count: { num }, hit: { hitCount } } = useSelector(state => state);
  //调用useDispatch进行分发action
  let dispatch = useDispatch();
  let addCount = () => {
      dispatch(add(1)); //省略store
  }
  ... //后面的方法照旧

注意:在回调函数中不能调用React Hook,ReactHook必须在React函数组件或自定义ReactHook函数中调用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/711261.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件测试缺陷

缺陷的定义 产品的定义不满足用户需求测试执行时&#xff0c;实际结果与预期结果不一致 缺陷产生的根本原因 需求变更沟通不畅&#xff0c;信息不同步软件复杂进度压力需求文档存在错误非根本设计存在错误非根本 缺陷的基本要素 ID编号&#xff1a;唯一模块&#xff1a;根据…

THREEJS 地图可视化案例分享

效果预览 git代码地址 threejs_map: threejs 地图可视化案例

能说会画 VisCPM:SOTA 开源中文多模态大模型

最近&#xff0c; 清华大学 NLP实验室、面壁智能、知乎联合在 OpenBMB 开源多模态大模型系列VisCPM &#xff0c;评测显示&#xff0c; VisCPM 在中文多模态开源模型中达到最佳水平。 VisCPM 是一个开源的多模态大模型系列&#xff0c;支持中英双语的多模态对话能力&#xff08…

全志科技T3国产工业核心板规格书(四核ARM Cortex-A7,主频1.2GHz)

1 核心板简介 创龙科技SOM-TLT3是一款基于全志科技T3处理器设计的4核ARM Cortex-A7国产工业核心板&#xff0c;每核主频高达1.2GHz。 核心板通过邮票孔连接方式引出CSI、TVIN、MIPI DSI、TVOUT、RGB DISPLAY、LVDS DISPLAY、GMAC、EMAC、USB、SATA、SDIO、UART、TSC、SPI、TW…

《PyTorch深度学习实践》第十讲 卷积神经网络(基础篇 + 高级篇)

b站刘二大人《PyTorch深度学习实践》课程第十讲卷积神经网络&#xff08;基础篇 高级篇&#xff09;笔记与代码&#xff1a; https://www.bilibili.com/video/BV1Y7411d7Ys?p10&vd_sourceb17f113d28933824d753a0915d5e3a90 https://www.bilibili.com/video/BV1Y7411d7Ys?…

数据结构--特殊矩阵的压缩存储

数据结构–特殊矩阵的压缩存储 一维数组的存储结构 ElemType a[10]; //ElemType型一维数组各数组元素大小相同&#xff0c;且物理上连续存放。 数组元素a[i]的存放地址 LOC i * sizeof(ElemType) ( 0 ≤ i < 10 ) (0\le i < 10) (0≤i<10) 注:除非题目特别说明&…

Win10不香了吗?微软 Win11 份额涨至 35.75% 创新高

根据 Valve 公司的最新调查结果&#xff0c;Windows 11 在 Steam 平台上的用户数量正稳步增长&#xff0c;预计将超过36%的比例。Steam定期进行用户软硬件情况调查&#xff0c;旨在提供可靠的数据支持供公司和游戏开发者用于未来的决策制定。此调查选择参与用户的方式是随机的&…

树莓派学习笔记12-安装使用PyZbar

树莓派学习笔记12-安装使用PyZbar 前言 通过树莓派外接USB摄像头&#xff0c;实现条形码的识别&#xff0c;并串口&#xff08;USB串口&#xff09;打印条形码的内容。 前期准备 硬件&#xff1a;树莓派4B 系统&#xff1a;Raspios-2021-03-04 编译器&#xff1a;Thonny 视…

如何基于GeoToolKit/INT实现矢量流线的聚集动画效果示例

继续在上一篇文章的基础上&#xff0c;利用相同的数据处理方法统一了不同年代地层的数据格式&#xff08;目前js解析支持的格式有ZMap、TS、XYZ和XYZA等&#xff09;&#xff0c;本文主要基于GeoToolKit/INT组件&#xff0c;针对地质研究经常在二维等值线基础上模拟计算地层中物…

【C++3】crontab,ftp

文章目录 1.生成数据&#xff1a;crontab2.ftp&#xff1a;ftp是tcp/ip协议族中一员&#xff0c;分客户端和服务端2.1 安装&#xff1a;linux操作系统的用户也是ftp的用户&#xff0c;可以配置专用的ftp用户&#xff0c;专用的ftp用户只能用于ftp&#xff0c;不能登录操作系统2…

ROS2移动机器人导航仿真

环境&#xff1a;Ubuntu 18.04 ros2 eloquent wget http://fishros.com/install -O fishros && . fishros1.安装turtlebot3相关功能包 sudo apt install ros-eloquent-turtlebot3* ref&#xff1a; https://docs.ros.org/ ROS2导航SLAM建图探索_鱼香ROS的博客-CSD…

蛋白组学 代谢组

https://www.cnblogs.com/yanzhi123/p/11712926.htmlhttps://www.cnblogs.com/yanzhi123/p/11712926.html【3】蛋白质组学鉴定软件之Mascot - 简书 (jianshu.com) 【6】蛋白质组学鉴定定量软件之MaxQuant - 简书 (jianshu.com) 基于Maxquant软件处理的LabelFree蛋白质组学 首…

图灵第4期MySQL调优专题学习笔记

目录 一、首先复习打印的课件 二、Explain中的列 三、解读extra 四、索引最佳实践 五、MySQL的内部组结构 2. bin-log归档&#xff1a; 六、常见SQL深入优化 1. order by 与 group by 优化 2. 索引设计原则 3. 分页查询优化&#xff08;根据非主键字段排序的分页查询…

Verilog基础之十三、ROM实现

目录 一、前言 二、非IP核设计 2.1 工程设计文件读取初始化 2.2 测试代码 2.3 仿真结果 三、IP核创建ROM 3.1 IP核生成ROM 3.2 设计代码 3.3 测试代码 3.4 仿真结果 四、modelsim设置 4.1 模拟信号显示 4.2 信号范围显示设置 五、数据文件 一、前言 对于工程中的…

IMX6ULL系统移植篇-uboot启动Log信息

一. 进入uboot 命令模式 只有启动 uboot&#xff0c;进入 uboot的命令行模式时&#xff0c;才能使用 uboot 的命令。 当开发板启动时&#xff0c;快速按下回车键即可进入 uboot命令行模式。这时&#xff0c;进入 uboot 的命令行模式以后输入“help” 或者 “&#xff1f;” &a…

基因遗传进化算法-找最优路径

import random import matplotlib.pyplot as pltplt.rcParams["font.sans-serif"]["SimHei"] #设置字体 plt.rcParams["axes.unicode_minus"]False #该语句解决图像中的“-”负号的乱码问题# 创建初始种群 def create_initial_population():popu…

1024天,CSDN上的时间之旅

1024天&#xff0c;CSDN上的时间之旅 感想收获未来规划职业规划创作规划 感想 今天是在CSDN这个博客上成为博主已经迈入了1024天。这个数字对于计算机领域来说&#xff0c;具有特殊的含义和重要性。 在计算机科学中&#xff0c;1024是2的十次方&#xff0c;也就是2^10。这意味…

rt-thread------串口V1(三)接收

系列文章目录 rt-thread 之 fal移植 rt-thread 之 生成工程模板 STM32------串口理论篇 rt-thread------串口V1版本&#xff08;一&#xff09;配置 rt-thread------串口V1版本&#xff08;二&#xff09;发送篇 文章目录 系列文章目录一、串口的接收中断接收DMA接收 一、串口…

从一次netty分享漫谈

从一次netty分享漫谈 1.前言 上周五&#xff0c;笔者所在的开发小组&#xff0c;组织了一场分享&#xff0c;内容是netty的入门。笔者所在的团队&#xff0c;基本上就是在各条业务线中活蹦乱跳&#xff0c;有经验的看官&#xff0c;到这里已经可以给出分享效果的总体预测&…

Gradle 各个版本下载

每次都要找下载地址&#xff0c;还是记录一下好找点。 http://services.gradle.org/distributions