react总结

一、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函数中调用

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

跨越时空限制,酷暑天气用VR看房是一种什么体验?

近年来&#xff0c;全球厄尔尼诺现象越来越频繁&#xff0c;夏季温度不断创下新高&#xff0c;持续大范围的高温天气让人们对出门“望而生畏”。很多购房者也不愿意在如此酷暑期间&#xff0c;四处奔波看房&#xff0c;酷暑天气让带看房效率大大降低&#xff0c;更有新闻报道&a…

VSCode+GDB+Qemu调试ARM64 linux内核

俗话说&#xff0c;工欲善其事 必先利其器。linux kernel是一个非常复杂的系统&#xff0c;初学者会很难入门。 如果有一个方便的调试环境&#xff0c;学习效率至少能有5-10倍的提升。 为了学习linux内核&#xff0c;通常有这两个需要 可以摆脱硬件&#xff0c;方便的编译和…

基于open62541库的OPC UA协议节点信息查询及多节点数值读写案例实践

目录 一、OPC UA协议简介 二、open62541库简介 三、 opcua协议的多点查询、多点读写案例服务端opcua_server 3.1 opcua_server工程目录 3.2 程序源码 3.3 工程组织文件 3.4 编译及启动 四、opcua协议的多点查询、多点读写案例客户端opcua_client 4.1 opcua_client工程目录 4…

使用 Jetpack Compose 构建 Spacer

欢迎阅读本篇关于如何使用 Jetpack Compose 构建 Spacer 的博客。Jetpack Compose 是 Google 的现代 UI 工具包&#xff0c;主要用于构建 Android 界面。其声明式的设计使得 UI 开发更加简洁、直观。 一、什么是 Spacer&#xff1f; 在 UI 设计中&#xff0c;我们通常需要在不…

CSS之平面转换

简介 作用&#xff1a;为元素添加动态效果&#xff0c;一般与过渡配合使用 概念&#xff1a;改变盒子在平面内的形态&#xff08;位移、旋转、缩放、倾斜&#xff09; 平面转换也叫 2D 转换&#xff0c;属性是 transform 平移 transform: translate(X轴移动距离, Y轴移动距…

【SpringCloud——Elasticsearch(下)】

一、数据聚合 聚合&#xff0c;可以实现对文档数据的统计、分析、运算。常见的聚合有三类&#xff1a; ①、桶聚合&#xff1a;用来对文档做分组 TermAggregation&#xff1a;按照文档字段值分组。Date Histogram&#xff1a;按照日期解题分组&#xff0c;例如一周为一组&am…

javaee sql注入问题

jsp页面 <% page language"java" contentType"text/html; charsetutf-8"pageEncoding"utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> &…

QT树的实现

理论 在Model/View结构中&#xff0c;数据模型为视图组件和代理组件提供存取数据的标准接口。在QT中&#xff0c;所有的数据模型类都从QAbstactItemModel继承而来&#xff0c;不管底层的数据结构是如何组织数据的&#xff0c;QAbstractItemModel的子类都以表格的层次结构表示数…

大数据需要一场硬件革命

光子盒研究院 计算领域的进步往往集中在软件上&#xff1a;华丽的应用程序和软件可以跟踪人和生态系统的健康状况、分析大数据&#xff0c;并在智力竞赛中击败人类冠军。与此同时&#xff0c;对支撑所有这些创新的硬件进行全面改革的努力相对来说&#xff0c;略显小众。 自2020…

Scala里的WordCount 案例

7.7.5 普通 WordCount 案例 package chapter07object TestWordCount__简单版 {def main(args: Array[String]): Unit {//单词计数&#xff1a;将集合中出现的相同单词计数&#xff0c;进行计数&#xff0c;取计数排名的前三的结果val stringList List("Hello Scala Hbas…

【数据可视化方案分享】电商数据分析

本文所分享的电商数据分析报表均来自奥威BI软件的电商数据分析方案&#xff01;该方案是一套包含数据采集、数据建模、数据分析报表的系统化、标准化数据分析方案&#xff0c;下载套用&#xff0c;立见效果&#xff01; 注意&#xff0c;奥威BI软件的电商数据分析方案分两类&a…

【基于Django框架的在线教育平台开发-01】账号登录及退出登录功能开发

文章目录 1 模型层开发2 视图层开发3 form表单验证4 配置urls.py5 模板层开发6 效果展示 1 模型层开发 用户数据表如下所示&#xff1a; FieldTypeExtraidintPrime Key & Auto Incrementpasswordvarchar(128)last_logindatetime(6)Allow Nullis_superusertinyint(1)usern…

mysql 常见锁类型

表锁 & 行锁 在 MySQL 中锁的种类有很多&#xff0c;但是最基本的还是表锁和行锁&#xff1a;表锁指的是对一整张表加锁&#xff0c;一般是 DDL 处理时使用&#xff0c;也可以自己在 SQL 中指定&#xff1b;而行锁指的是锁定某一行数据或某几行&#xff0c;或行和行之间的…

第二章 数据处理篇:transforms

教程参考&#xff1a; https://pytorch.org/tutorials/ https://github.com/TingsongYu/PyTorch_Tutorial https://github.com/yunjey/pytorch-tutorial 详细的transform的使用样例可以参考&#xff1a;ILLUSTRATION OF TRANSFORMS 文章目录 为什么要使用transformstransforms方…

二叉树题目:单值二叉树

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;单值二叉树 出处&#xff1a;965. 单值二叉树 难度 3 级 题目描述 要求 如果二叉树每个结点都具有相同的值&am…

SQL死锁

目录 前言&#xff1a; 分析&#xff1a; 死锁产生的原因&#xff1a; sql死锁 模拟&#xff1a; 解决办法&#xff1a; (本质&#xff1a;快速筛选或高效处理、以此减少锁冲突) ①大事务拆成小事务&#xff0c;尽可能缩小事务范围 大事务:将多个操作放在一个事务中执行…

【MOOC 测验】第5章 链路层

1、局域网的协议结构一般不包括&#xff08; &#xff09; A. 数据链路层B. 网络层C. 物理层D. 介质访问控制层 逻辑链路控制子层、介质访问控制子层、物理层 2、下列关于二维奇偶校验的说法&#xff0c;正确的是&#xff08; &#xff09; A. 可以检测和纠正双比特差错B…

【CVRP测评篇】 算法性能如何?来测!

我跨越了2100015秒的距离&#xff0c;为你送上更全面的算法性能评测。 目录 往期优质资源1 CVRP数据集2 实验准备2.1 计算机配置2.2 调参方法2.3 参数设定2.4 实验方法 3 实验结果3.1 最优解统计3.1.1各数据集上的算法性能对比3.1.2 求解结果汇总3.1.3小结一下3.1.4 还有话说 3…

【软考网络管理员】2023年软考网管初级常见知识考点(10)- 网际协议IP及IPV6,IPV4详解

涉及知识点 分类的IP地址&#xff0c;子网划分&#xff0c;CIDR和路由汇聚&#xff0c;IPV4数据报格式&#xff0c;IPV6协议&#xff0c;软考网络管理员常考知识点&#xff0c;软考网络管理员网络安全&#xff0c;网络管理员考点汇总。 原创于&#xff1a;CSDN博主-《拄杖盲学…

剑指 Offer 68 - II. 二叉树的最近公共祖先 / LeetCode 236. 二叉树的最近公共祖先(搜索与回溯)

题目&#xff1a; 链接&#xff1a;剑指 Offer 68 - II. 二叉树的最近公共祖先&#xff1b;LeetCode 236. 二叉树的最近公共祖先 难度&#xff1a;中等 上一题博客&#xff1a;剑指 Offer 68 - I. 二叉搜索树的最近公共祖先 / LeetCode 235. 二叉搜索树的最近公共祖先&#xf…