React组件——类组件

news2024/11/17 9:32:47

一、组件的概念

使用组件的方式进行编程,可以提高开发效率,提高组件的复用性、提高代码的可维护性和可扩展性

React定义组件的方式有两种

类组件:React16.8版本之前几乎React使用的都是类组件

函数组件:React16.8之后,函数式组件使用的越来越多

二、组件的定义的两种方式

1、ES6类的回顾

类组件:类组件是指使用ES6中class定义的组件称为类组件

class 类名 {

}

类是由属性和方法组成

类中的属性:表示事务的特征

类中的方法:表示的是对象的行为

class 类名{
    属性名1;
    属性名2;
    方法1(){}
    方法2(){}
}

案例1:类的定义

/*
定义一个学生类
    class 类名{}
    类名的规定
    1、类名是由字母、数字、下划线或者$符号组成
    2、名称不能以数字开头
    3、类名直接不能有空格、不能是关键字或者保留字
    4、不能是true、false、null
    5、类名采用驼峰是命名法,每个单词的首字母要大写
    6、要见名知意
*/
class Student {
    sno;//学号
    sname;//姓名
    gender;//性别
    education;//学历
    major;//专业
    introduce(){
        return `学号:${this.sno}\n姓名:${this.sname}\n性别:{this.gender===1?'男':'女'}\n学历:${this.education}\n专业:${this.major}\n`
    }
}
/*
    创建对象的语法
    const/let 对象名 = new 类名()
    给对象赋值的语法
    对象名.属性名=值
    调用对象中的方法
    对象名.方法名()
*/

案例2:构造方法的使用

class Student{
    constructor(sno,sname,gender,education,major){
        this.sno=sno
        this.sname=sname
        this.gender=gender
        this.education=education
        this.major=major
    }
    introduce(){
        return `学号:${this.sno}\n姓名:${this.sname}\n性别:${this.gender}\n学历:${this.education}\n专业:${this.major}`
    }
}

/*
    实例化对象的同时进行初始化
    let/const 对象名=new 类名(实参1,实参1,....,实参n)
 */
const s1=new Student('1001','张三','男','专科','通讯工程')
console.log(s1.introduce());
console.log("*********************************************");
const s2=new Student('1002','李四','男','专科','英语')
console.log(s2.introduce());

案例三:继承的使用

class Teacher{
    constructor(name,school){
        this.name=name
        this.school=school
    }
    introduce(){
        return `我是${this.school}的${this.name}`
    }
    giveLession(){
        console.log('讲解本章目标');
        console.log('讲解本章内容');
        console.log('进行本章总结');
        console.log('安排今日作业');
    }
}

/**
 * 定义一个子类
 * 继承的语法
 * class 子类的名称 extends 父类名称{
 * }
 * super关键字的使用
 * suepr()直接调用父类的构造方法,它的位置必须放在子类构造方法的首行
 * super.父类中的方法/父类中的属性
 * 方法的重写:是在继承关系中,子类中的方法名和父类中的方法名,参数个数相同的这么一种情况,称为方法的重写
 * 方法重写的结果就是子类中的内容完全覆盖父类中方法中的内容
 */
class WebTeacher extends Teacher{
    constructor(name,school){
        super(name,school)
    }
    giveLession(){
        console.log('首先打开vscode开发环境');
        super.giveLession()
    }
}

class TestTeacher extends Teacher{
    constructor(name,school){
        super(name,school)
    }
    giveLession(){
        console.log('打开postman或者vm虚拟机');
        super.giveLession()
    }
}

let zhangsan=new WebTeacher('张三','xxx')
console.log(zhangsan.introduce()); 
zhangsan.giveLession()
console.log('********************************');

let lisi=new WebTeacher('李四','xxx')
console.log(lisi.introduce());
lisi.giveLession()

console.log('**************************************');

const wanger=new TestTeacher('王二','xxx')  

console.log(wanger.introduce())
wanger.giveLession()

2、定义类组件

类组件:是指通过ES6类来定义的组件称为类组件,React中定义类组件有如下约定

类组件首字母大写

类组件必须要继承React.Component父类,这个父类中的相关方法和属性都能被继承

类组件中必须要有一个render方法

这个render必须要有返回值,返回值的内容就是这个类组件的结构(jsx)

由于这个类组件要被别的组件引用,所以使用ES6的默认导出将其导出,便于别的组件引用

import React from "react"
export default class Hello extends React.Component{
    render(){
        return(
        <>
          <h1>Hello组件</h1>
        </>) 
    }
}

类组件定义之后,引用这个类组件

import ReactDOM from 'react-dom/client'
import Hello from './components/Hello'
const template=(<>
    <Hello></Hello>
</>)
const root=ReactDOM.createRoot(document.querySelector('#root'))
root.render(template)

3、定义函数组件

在React中除了类组件之外,也可以定义函数组件

函数租价:所谓函数组件是指通过普通函数或者箭头函数所定义出来的组件称为函数组件

函数组件有如下规定

函数名必须首字母大写

使用ES6的export将默认导出,便于别的组件引用

函数必须要有一个返回值,这个返回的内容是JSX,返回的是该函数组件的结构

export default function HelloWorld(){
    return (
        <>
            <h1>Hello World函数组件</h1>
        </>)
}

注意:如果不返回任何内容,假设返回一个null,页面将没有任何内容

在实际开发过程中,由于这些组件和函数组件它的结构都是固定的,所以可以使用一些插件将其生成出来

 使用rcc生成类组件,使用rfc生成函数组件

三、类组件的事件处理

1、React的事件处理

vue中通过什么绑定事件处理函数

v-on指令来实现绑定,可以通过他的简写@事件类型方式来绑定事件的

在React中通过onClick属性来实现单击事件的绑定的,常见的写法

直接在标签中通过onClick={()=>{}}来实现事件的绑定

在标签中通过onClick={this.类中的普通成员方法}来进行绑定

在标签中通过onClick={this.类中的箭头函数}来进行绑定

在标签中通过onClick={()=>{this.函数名称()}}

import React,{Component} from 'react'

export default class Hello extends Component {

handleClick(){
    console.log("类中的定义的普通方法",this)
}
handleClick2=()=>{
    console.log("类中定义的箭头函数",this)
}
render(){
    return (
     <>
        <button onClick={()=>{
          console.log('我是按钮1,我被点击了~~~~');
        }}>按钮1</button>

        <button onClick={this.handleClick}>按钮2</button>
        <button onClick={this.handleClick2}>按钮3</button>
        <button onClick={()=>{this.handleClick2()}}>按钮4</button>
      </>
    )
   }
}

2、this指向的回顾

let teacher={
    name:'teacher'
}
let student={
    name:'student'
}
let person={
    name:'person',
    show(age,sex){
        return `我叫${this.name},今年${age}岁,我的性别是${sex}`
    }
}
console.log(person.show(38,'男'))
//改变this指向,改变this执行的方式有三个,第一个是call,call的作用调用函数,还可以改变this执行 
console.log(person.show.call(teacher,48,'女')); 
//通过apply的方式也可以调用函数,这种方式调用方法同时,改变this指向
console.log(person.show.apply(student,[22,'男'])); 
//通过bind的方式来改变this指向
let teaherShow=person.show.bind(teacher)
console.log(teaherShow(55,'男')); 

如果使用第二方式来进行事件绑定的时候,会存在this执行为空的情况,解决办法如下

  • 使用箭头函数写法代替普通方法(建议)
  • 通过bind方式来改变this执行(不建议)

this执行改变的代码可以写在多个位置,比如写在构造函数中(经典的写法)

export default class Hello extends Component {
  constructor(){
    super()
    //改变this执行
    this.handleClick=this.handleClick.bind(this)
  }
}

也可以在调用的同时去改变this指向(不建议)

 <button onClick={this.handleClick.bind(this)}>按钮2</button>

3、事件传参

import React, { Component } from 'react'
/*
  React的事件传值的形式有如下三种
  1、进行事件传值的时候,没有实参,默认形参接收的event对象
  2、进行事件调用的同时,传递额外的参数
*/
export default class Hello extends Component {
  handleClick=(e)=>{
    console.log('e',e);
  }
  handleClick2=(arg1,arg2,arg3)=>{
    console.log('参数1:',arg1);
    console.log('参数2:',arg2);
    console.log('参数3:',arg3);
  }
  handleClick3=(arg1,arg2,arg3,arg4)=>{
    console.log('参数1:',arg1);
    console.log('参数2:',arg2);
    console.log('参数3:',arg3);
    console.log('参数4:',arg4);
  }
  render() {
    return (
      <>
        <button onClick={this.handleClick}>按钮1</button>
        <button onClick={()=>{this.handleClick2(22,33,56)}}>按钮2</button>
        <button onClick={(e)=>{this.handleClick3(89,e,99,78)}}>按钮3</button>
      </>
    )
  }
}

四、类组件的state

vue框架和React框架最大的一个好处就是不需要开发人员去操作DOM,只要大家操作了数据,自动DOM元素会发生改变,这种操作称为响应式

在vue中响应式数据主要来自两个部分

        组件内部响应式数据是定义在data选项

        来自子组件外部通过props来定义的

在React中也是一样,如果要定义响应式数据,组件内部的数据是定义在组件的state中,组件外部的数据是定义在props中

1、有状态组件和无状态组件

类组件是有状态组件:因为一个组件的状态是存放在类的实例上,state,props都是存在this上,所以类组件被称为有状态组件

函数组件是无状态组件:函数组件都没有this,函数不能存放状态

类组件比较强大,函数组件比较单一,类组件可以完成复杂的功能,但是函数组件是简单的组件

在React16.8版本之后引入hooks可以让函数组件也能操作状态

总结:React16.8之前函数组件是无状态组件,几乎很少使用

2、基本使用步骤

使用state定义数据一共有三步骤

第一步:定义数据

定义数据可以在构造函数内部定义,也可以在构造函数外部定义

第二步:获取数据

在使用数据的时候为了提高读取性能,最好使用解构赋值方式

第三步:修改数据

修改数据的时候一定要使用setState({})来修改数据,这个方法是一个异步方法

第1步、定义数据

  • 定义数据的时候可以在构造函数中定义数据,如下所示
class Counter extends React.Component{
    constructor(){
        super();
        this.state={
            count:0
        }
    }
    render(){
        return (
            <div>
             <h2>计数器</h2>
            </div>
        )
    }
}
export default Counter;
  • 也可以在构造函数外部定义,这种是利用ES6属性的简化语法,如下所示
class Counter extends React.Component{
    //简化语法
    state={
       count:0
    }
    render(){
        return (
            <div>
                <h2>计数器</h2>
            </div>
        )
    }
}
export default Counter;

第2步、获取数据

通过this.state获取数据

class Counter extends React.Component{
    constructor(){
        super();
        this.state={
            count:0
        }
    }
    render(){
        return (
            <div>
                <h2>计数器</h2>
                <span>{this.state.count}</span>
             </div>
        )
    }
}
export default Counter;

在使用数据的时候,最好使用解构赋值的方式,这样能够提高性能

import React, { Component } from 'react'

export default class Counter extends Component {
    state = {
        num: 0
    }
    constructor() {
        super()
    }
    render() {
        const { num } = this.state;
        return (
            <div>
                <h1>计数器</h1>
                <span>{num}</span>
            </div>
        )
    }
}

第3步、修改数据

        状态是可以改变的

        语法:this.setState({要修改的数据})

注意:不要直接修改state中的数据,这样是错误的

        setState()作用:1.修改state 2.更新UI

import React, { Component } from 'react'

export default class Counter extends Component {
    state = {
        num: 0
    }
    constructor() {
        super()
    }
    render() {
        const { num } = this.state;
        return (
            <div>
                <h1>计数器</h1>
                <span>{num}</span>
                <button onClick={() => {
                    this.setState({
                        num: this.state.num + 1
                    })
                }}>+1</button>
            </div>
        )
    }
}

3、购物车案例

实现步骤

  • 在compotents下创建ShopcartList.jsx,并在App.jsx中引入这个自定义组件
  • ShopcartList.jsx中的关键代码如下
import React, { Component } from 'react'
import '../assets/css/shopcartList.scss'

export default class ShopcartList extends Component {
  constructor() {
    super()
    //定义状态数据

this.state = {
      shopcartList: [
        {
          pid: '1001',
          pname: '欧莱雅男士护肤',
          price: 38,
          num: 1
        },
        {
          pid: '1002',
          pname: 'OLAY女士防皱润肤露',
          price: 108,
          num: 1
        },
        {
          pid: '1003',
          pname: '自然堂女士护肤',
          price: 108,
          num: 2
        },
        {
          pid: '1004',
          pname: '兰蔻香水',
          price: 1038,
          num: 1
        },
        {
          pid: '1005',
          pname: '大宝SOD蜜',
          price: 8,
          num: 1
        }
      ]
    }
  }
//改变数量的方法
changeNum = (sign,index) => {
    switch(sign) {
        case '+':
       //如下操作,只能将数据进行更新,但不会将页面进行变化
        this.state.shopcartList[index].num++
        break
        case '-':
        if(this.state.shopcarList[index].num > 1){
            this.state.shopcarList[index].num--
        }else{
            window.alert("数量不能少于0")
        }
        break
    }
    this.setState({
        shopcartList:this.state.shopcartList
    })
}
//计算总价的函数
total = ary => `${ary.reduce((pre,cur)=>pre + cur.price*cur.num,0).toFixed(2)}`
//删除的方法
deleteShopcarList = index =>{
    if(window.confirm("您确定要删除吗?")){
        //数组中的splice方法的参数含义
        //参数1:表示要操作的数组的下标
        //参数2:表示要删除几个数据
        this.state.shopcartList.splice(index,1)
        //使用this.setState来更新页面
        this.setState({
            shopcartList:this.state.shopcartList
        })
    }
}

render(){
    const {shopcartList} = this.state
    return (
    <div>
        <h1>购物车</h1>
        <table>
            <thead>
                <tr>
                    <td>序号</td>
                    <td>名称</td>
                    <td>价格</td>
                    <td>数量</td>
                    <td>小计</td>
                    <td>操作</td>
                </tr>
            </thead>
            <tbody>
            {
                shopcartList.map((item,index)=><tr key={item.id}>
                    <td>{item.pid}</td>
                    <td>{item.pname}</td>
                    <td>{item.price}</td>
                    <td>
                       <button onClick={()=>{this.changeNum("-",index)}}>-</button>
                        {item.num}
                       <button onClick={()=>{this.changeNum("+",index)}}>+</button>
                    </td>
                    <td>{item.price*item.num}</td>
                    <td>
                       <button onClick={()=>{this.deleteShopcartList(index)}}>删除                        </button>
                    </td>
                </tr>)
            }
            </tbody>
            <tfoot colSpan={5}>
                {this.total(shopcartList)}
            </tfoot>
        </table>
    </div>)
    }
}
    



4、setSate是同步还是异步

需要分情况

        在react18之后setState是异步的,

        如果是react18之前,setState根据情况来决定,可能是同步也可能是异步的

在react18版本之后

        在react的事件处理中(合成事件),setState是异步

        在setTimeout、setInterval、原生js中它也是异步的(重点区别)

        如果在react18版本中将this.setState由异步变成同步,需要使用flushSync

componentDidMount() {
    console.log('1、', this.state.count);
    document.querySelector('#btn').addEventListener('click', () => {

      flushSync(()=>{{
        this.setState({
          count: this.state.count + 1
        })
        console.log('2、', this.state.count);
      }})
     
      flushSync(()=>{
        this.setState({
          count: this.state.count + 1
        })
        console.log('3、', this.state.count);
      })
     
    })
  }

react18版本之前

        在react事件处理中(合成事件),setState是异步的

        在setTimeout、setInterval、原生js中它是同步的

5、setState的另一种写法

setState((state,props)=>{},()=>{})

五、类组件的props属性

组件中的数据,除了组件内部的状态使用state之外,状态也可以来自组件的外部,外部的状态使用类组件实例上另外一个属性来表示props

1、基本的使用

在components下创建UserInfo组件

import React, { Component } from 'react'
import '../assets/css/userinfo.scss'
export default class UserInfo extends Component {
  render() {
    return (
      <div className='info-box'>
          <h3>{this.props.title}</h3> 
          <div>姓名:{this.props.name}</div>
          <div>年龄:{this.props.age}</div>
          <div>性别:{this.props.gender===1?'男':'女'}</div>
          <div>爱好:{this.props.hobby.join("、")}</div> 
      </div>
    )
  }
}

在App.jsx中引用组件,并且传递数据到组件的内部

import React, { Component } from 'react'
import UserInfo from './components/UserInfo'
export default class App extends Component {
  constructor(){
    super()
    this.state={
      users:[
        {
          title:'教师信息',
          name:'张三',
          age:58,
          gender:1,
          hobby:['爱打游戏','爱读书']
        },
        {
          title:'学生信息',
          name:'李四',
          age:24,
          gender:1,
          hobby:['爱写js','爱UI设计']
        },

        {
          title:'班主任信息',
          name:'王麻子',
          age:28,
          gender:0,
          hobby:['爱唱歌','爱学习']
        }
      ]
    }
  }
  render() {
    const {users}=this.state
    return (
      <>
         {
            users.map((item,index)=><UserInfo key={index} {...item}/>)
         }
      </>
    )
  }
}

2、props的默认值

React的props的默认值设置有两种方法

  • 类名.defaultProps={key:value}
export default class UserInfo extends React.Component{}
UserInfo.defaultProps={
  title:'暂无标题',
  name:'暂无名称',
  age:0,
  gender:1,
  hobby:['暂无兴趣'],
  birthday:new Date(),
  avatar:'http://old.woniuxy.com/page/img/head_picture/0.jpg'
}
  • static defaultProps={key:value}(推荐)
export default class UserInfo extends React.Component{
    static defaultProps={
        title:'暂无标题',
        name:'暂无名称',
        age:0,
        gender:1,
        hobby:['暂无兴趣'],
        birthday:new Date(),
        avatar:'图片路径'
     }
}

3、props的只读性

总结:

  • 通过props传递进来的基本类型,组件内部不能直接更改,如果更改就会报如下错误

  • 通过props传递进来的引用数据,不能直接更改,如果更改会报如下错误

  • 通过props传递进来的引用数据类型,可以更改引用数据类型的属性

六、表单组件

表单的处理:从表单中获取内容

vue中如果获取表单的内容:v-model:是一个双向绑定的指令,react不是一个双向绑定

1、受控表单

1.1、什么是受控组件

在HTML中,表单元素(如<input>、<testarea>、和<select>)之类的表单元素同茶馆自己维护state,并根据用户输入进行更新。而在React中,可以改变状态(mutable state)通常保存在组件的state属性中,并且只能通过setState()来更新

我们可以把两者结合起来,使React的state成为“唯一的数据源”。渲染表单的React组件还控制着用户输入过程中表单发生的操作。被React以这种方式控制取值的表单输入严肃叫做“受控组件”

1.2、受控组件的使用步骤

   在state中添加一个状态name

state={
    name:"
}

 我们在input标签里面定义了value属性,这个属性就是文本框的值,将state对象里面的name绑定到文本框里

<input value={this.state.name}/>

onChange是必要的,这个函数相当于绑定了一个事件,当数据发生变化的时候,可以执行帮的的函数handleName

<input value={this.state.name} onChange={this.handleName}/>

定义handleName函数,将文本框的值动态绑定赋值给state对象

handleName=(e)=>{
    this.setState({
        name:e.target.vale
    })
}

参数e代表事件对象,可以通过e来获取到target目标对象,通过value值来获取文本框的值。

2、非受控表单

有时使用受控组件会很麻烦,因为你需要为数据变化的每种方式都编写事件处理函数,并通过一个 React 组件传递所有的输入 state。当你将之前的代码库转换为 React 或将 React 应用程序与非 React 库集成时,这可能会令人厌烦。在这些情况下,你可能希望使用非受控组件, 这是实现输入表单的另一种方式。

在大多数情况下,我们推荐使用 受控组件 来处理表单数据。在一个受控组件中,表单数据是由 React 组件来管理的。另一种替代方案是使用非受控组件,这时表单数据将交由 DOM 节点来处理。

要编写一个非受控组件,有两种方式

2.1、第1种方式

使用步骤

  • 在表单元素中添加ref属性
<form onSubmit={this.register}>
    <div>
        <label>用户名:</label>
        <input type="text" placeholder='请输入用户名' ref={input=>this.nameEle=input}/>
    </div>
     <div>
         <input type="submit" value="提交"></input>
     </div>
</form>
  • 在通过this.nameEle.value来获取到当前节点的值
register=(e)=>{
    console.log(this.nameEle.value);
    e.preventDefault();
}

2.2 、第2种方式

使用步骤

  • 调用React.createRef()方法创建一个ref对象
constructor(){
    super();
    this.username=React.createRef();
}
  • 将创建好的ref对象添加到文本框中
 <form onSubmit={this.register}>
     <div>
         <label>用户名:</label>
         <input type="text" placeholder='请输入用户名' ref={this.username}/>
      </div>
      <div>
          <input type="submit" value="提交"></input>
      </div>
</form>
  • 通过ref对象获取到文本框的值
 register=(e)=>{
     console.log(this.username.current.value);
     e.preventDefault();
 }

七、组件的生命周期

生命周期指的就是从组件的加载初始化-数据的改变-组件的卸载阶段。描述的就是组件从创建到死亡的阶段,react的生命周期中提供了很多个生命周期钩子函数来方便我们操作。

react的生命周期主要分为以下的几个步骤:

1、挂载阶段:组件数据的初始化,及组件数据的渲染

2、运行阶段:这个节点是最长的阶段,主要用户对数据进行修改,进行状态改变以及重绘

3:卸载阶段:这个阶段也是销毁阶段,组件运行完成后,或者从页面移除,那么组件就应该被销毁,这个阶段我们可以执行一些资源的回收,性能优化的代码,在此阶段完成

组件的生命周期流程图

1、组件挂载阶段

组件的挂载阶段也是组件创建-数据初始化-数据渲染的过程。接下来通过代码来演示执行的流程。

1.1 constructor构造器执行的阶段

构造执行那就意味着组件正在被创建,并且数据也可以在这里初始化。

通常,在 React 中,构造函数仅用于以下两种情况:

  • 通过给 this.state 赋值对象来初始化内部 state
  • 为事件处理函数绑定实例

如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。

先执行constructor构造器将数据初始化,其中props和state的数据就会被加载。接着执行render函数来渲染。

1.2 componentDidMount函数执行

界面渲染完毕(DOM挂载完毕)的一个通知,类似于vue组件中的created,我们可以在这个生命周期做

很多事情。

  • 获取DOM节点
  • 发送请求获取后台数据
  • 设置定时器、延时器等等
  • 绑定全局事件,例如document的点击事件等等
 async componentDidMount(){
    console.log("===componentDidMount==");
    const {data:{movies}}=await          
                          axios.get('https://www.fastmock.site/mock/4441a79ad5f884f83b784cf4c588bdb6/movies/getHotMovies')
  }

2、组件更新阶段

每当组件的props或者state变化之后,都会导致组件的更新,我们可以使用钩子函数在组件更新之前或者之后做一些逻辑操作。

2.1、创建shouldComponentUpdate钩子函数来拦截数据修改

shouldComponentUpdate(){
     return false;
}

定义在重新render之前被调用,可以返回一个布尔值来决定一个组件是否更新, 如果返回false,那么前面的流程都不会被触发。这个方法默认的返回值都是true。

这里我们再来完成一个功能就是将外部传递进来的数据赋给内部变量data,当点击按钮的时候每次增加1

import React, { Component } from 'react'
import axios from 'axios'
export default class LifeCycle extends Component {
  constructor(props){
    super(props)
    this.state={title:props.title}
  }
  changeTitle=()=>{
    this.setState({
      title:"计数器演示"
    })
  }
  render() {
    console.log("====调用render方法开始执行数据渲染");
    return (
      <div>
        <h1>{this.state.title}</h1>
        <button onClick={this.changeTitle}>更改标题</button>
      </div>
    )
  }
  shouldComponentUpdate(nextProps,nextState){
    console.log(this.state.title+"---------->"+nextState.title);
    return true
  }
}

但是从控制台的显示结果来看,每次点击之后都是上次的数据,如果要想得到更改后的数据我们可以使用如下参数来解决这个问题

参数:nextProps代表更改后的props值;nextState代表更改过后的state值。我们可以获取到这个数据进行判断

shouldComponentUpdate(nextProps,nextState){
    console.log(this.state.data+"---->"+nextState.data);
    return true;
}

控制台显示效果如下所示

2.3 componentDidUpdate钩子函数来执行通知。

componentDidUpdate(){
 console.log("数据修改成功");
}

3、组件卸载阶段

componentWillUnmount,在组件被卸载和销毁之前调用的方法,可以在这里做一些清理的工作。我们要完成组件的卸载,那需要创建两个组件,在父组件中引入子组件,当条件为true的时候就加载组件,条件为false的时候就卸载组件。

export default class ParentCom extends Component {
    state = {
        isShow:true
    }
    changePanel = ()=>{
        this.setState({
            isShow:false
        })
    }
    render() {
        return (
            <div>
                <h1>ParentComponent</h1>
                {this.state.isShow && <ChildCom/>}
                {/* {this.state.isShow && } */}
                <button type="button" onClick={this.changePanel}>点击切换</button>
            </div>
        )
    }
}

花括号中嵌入逻辑与 (&&) 运算符。它可以很方便地进行元素的条件渲染,&&是我们的短路运算符,一旦this.state.boo的值为false,那后面<ChildCom>的组件就不会渲染。

当点击按钮我们动态修改isShow的值,然后判断条件false组件就卸载。

export default class ChildCom extends Component {
    render() {
        return (
            <div>
                <h2>ChildComponent</h2>
            </div>
        )
    }
    componentWillUnmount(){
        console.log("ChildCom组件正在卸载");
    }
}

一旦组件卸载那就执行componentWillUnmount钩子函数完成调用,那我们可以完成清理工作。

执行结果为:

ChildCom组件正在卸载

4、资源清理

在componentWillUnmount钩子函数中我们可以执行一些资源清理工作。比如事件解绑,定时器清除等等工作。

先在ChildCom子组件里面定义事件和定时器等等代码。

componentDidMount(){
    this.timer = setInterval(function(){
     console.log("定时器执行");
    },1000);
}

在componentDidMount组件里面设置一个定时器,我们在父组件里面将组件卸载。但是定时器依然在执行所以,所以在组件卸载的时候我们需要清楚定时器。

componentWillUnmount(){
    console.log("ChildCom组件正在卸载");
    clearInterval(this.timer);
}

执行完组件的卸载,那定时器就被清除。

八、兄弟组件通信

  • 状态提升
  • 事件总线
  • 发布订阅模式

1、状态提升

React中的状态提升概括来讲,就是将多个组件需要共享的状态提升到他们最近的父组件上,再在父组件上改变这个状态,然后通过props分发给子组件

2、事件总线方式

兄弟之间传递参数我们有多种方案,本节中我们就给大家带来。基于事件总线的方式来设计

EventBus 又称为事件总线。在Vue中可以使用 EventBus 来作为沟通桥梁的概念,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所以组件都可以上下平行地通知其他组件,但也就是太方便所以若使用不慎,就会造成难以维护的灾难,因此才需要更完善的Vuex作为状态管理中心,将通知的概念上升到共享状态层次。

但在React中没有EventBus的概念,可以通过 node events模块进行模拟,在React中可以依赖一个使用较多的库 events 来完成对应的操作。

1)安装events

yarn add events

2)创建事件中心

新建一个文件MyEventListener.js文件用于作为事件中心。

import { EventEmitter } from 'events';
export default new EventEmitter();

我们只需要引入EventEmitter对象,将这个对象实例化过后返回出去,调用的时候执行对应的API就可以完成事件的绑定和通知

events常用的API:

  • 创建EventEmitter对象:eventBus对象;

  • 发出事件:eventBus.emit("事件名称", 参数列表);

  • 监听事件:eventBus.addListener("事件名称", 监听函数);

  • 移除事件:eventBus.removeListener("事件名称", 监听函数);

3)监听事件

import React, { Component } from 'react'
import MyEventListener from './MyEventListener'

export default class Borther1 extends Component {
    state={
        className:'web05'
    }
    //在组件完毕后就监听dataChange事件
    componentDidMount(){
        MyEventListener.addListener("dataChange",this.changeData);
    }
    //当组件卸载的时候就移除事件监听
    componentWillUnmount(){
        MyEventListener.removeListener('dataChange',this.changeData);
    }
    changeData=(params)=>{
        this.setState({
            className:params
        });
    }
    render() {
        return (
            <div>
                <h2>兄弟1</h2>
                <p>数据为:{this.state.className}</p>
            </div>
        )
    }
}

在生命周期函数里面绑定事件监听和移除事件监听,当事件中心里面加入了dataChange事件,当前组件就能监听到,接着执行我们绑定的changeData。

4)发送事件

在Brother2组件里面我们添加一个按钮,往事件中心发出一个dataChange事件,并将值传递给调用者

import React, { Component } from 'react'
import MyEventListener from './MyEventListener'

export default class Borther2 extends Component {
    changeData=()=>{
        MyEventListener.emit("dataChange","web08");
    }
    render() {
        return (
            <div>
                <h2>兄弟2</h2>
                <button onClick={this.changeData}>修改数据</button>
            </div>
        )
    }
}

其中emit这个API就可以完成事件发送。这样就可以完成兄弟组件之间的参数传递,当然事件总线这种设计模式可以应用在任何组件之间。实现跨组件通信。

3、发布与订阅模式

1)定义订阅者

//创建一个observer.js文件
const observer={
    list:[],
    subscribe(callback){
        this.list.push(callback)
    },
    dispatch(data){
        this.list.forEach(item => {
            item(data)
        });
    }
}
export default observer

2)定义子组件用来发送数据

import React, { Component } from 'react'
import './son.css'
import observer from '../observer'
export default class Son extends Component {
  render() {
    return (
      <div className='sonbox'>
        <h2>儿子</h2>
        <button onClick={()=>{
          observer.dispatch('兄弟姐妹们,我给每个人200元') 
        }}>发送</button>
      </div>
    )
  }
}

3)定义子组件用来接受数据

import React, { Component } from 'react'
import './daughter.css'
import observer from '../observer'
export default class Daughter extends Component {
  state={
    fromMsg:''
  }
  componentDidMount(){
    observer.subscribe(data=>{
      this.setState(()=>{
        return{
          fromMsg:data
        }
      })

    })
  }
  render() {
    return (
      <div className='daughterbox'>
        <h2>女儿</h2>
        <div>{this.state.fromMsg}</div>
      </div>
    )
  }
}

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

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

相关文章

2023高频前端面试题(含答案)

一、简单页面 1、CSS选择器样式优先级 2、CSS实现三列布局&#xff08;左右固定宽度&#xff0c;中间自适应&#xff09; &#xff08;1&#xff09;CSS浮动 第一个float:left&#xff0c;第二个float:right&#xff0c;第三个设置margin-left和margin-right &#xff08;2&am…

什么是Mixin?带你了解Vue中的Mixin混入

什么是Mixin&#xff1f;本篇文章带大家了解一下Vue中的Mixin混入&#xff0c;介绍Mixin和Vuex的区别&#xff0c;Mixin的使用方法&#xff0c;希望对大家有所帮助&#xff01; Vue框架现在基本上已经占据了前端的半壁江山&#xff0c;Vue的数据驱动和组件化的思想深入人心。Vu…

web前端-JavaScript中的数组详解

&#x1f41a;作者简介&#xff1a;苏凉&#xff08;专注于网络爬虫&#xff0c;数据分析&#xff0c;正在学习前端的路上&#xff09; &#x1f433;博客主页&#xff1a;苏凉.py的博客 &#x1f310;系列总专栏&#xff1a;web前端基础教程 &#x1f451;名言警句&#xff1a…

我用尽了洪荒之力,解开了ChatGPT 写前端代码的封印,结果...

我用尽了洪荒之力&#xff0c;解开了ChatGPT 写前端代码的封印介绍ChapGPT 听起来好得令人难以置信&#xff0c;所以让我们让它为我们编写一些 JS 代码。我想看看它是否可以解决我作为前端开发人员每天所做的任务。是驴子是马拉出来溜溜&#xff0c;我们还是直接进入主题一探究…

Vite + Vue3 +Vant4构建项目时,按需引入使用Toast组件,引用 showToast 时出现编译报错的解决方案

Vite Vue3 Vant4构建项目时&#xff0c;按需引入使用Toast组件&#xff0c;引用 showToast 时出现编译报错的解决方案 文章目录Vite Vue3 Vant4构建项目时&#xff0c;按需引入使用Toast组件&#xff0c;引用 showToast 时出现编译报错的解决方案一.问题定位二.以下为完整解决…

React+Mobx|基本使用、模块化

欢迎来到我的博客 📔博主是一名大学在读本科生,主要学习方向是前端。 🍭目前已经更新了【Vue】、【React–从基础到实战】、【TypeScript】等等系列专栏 🛠目前正在学习的是🔥 R e a c t 框架 React框架 Reac

【小程序项目开发 -- 京东商城】uni-app 商品分类页面(上)

&#x1f935;‍♂️ 个人主页: 计算机魔术师 &#x1f468;‍&#x1f4bb; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f310; 推荐一款找工作神器网站: 点击跳转牛客网 |笔试题库|面试经验|实习招聘内推| 还没有账户的小伙伴 速速点击链接…

前端 --- HTML

文章目录1. HTML 结构1.1 HTML 文件基本结构1.2 VsCode 中 使用 HTML 的快捷键2. HTML 常见标签2.1 注释标签2.2 标题标签2.3 段落标签2.4 换行标签2.5 格式化标签2.6 图片标签2.7 超链接标签2.8 表格标签2.9 列表标签2.10 表单标签form 标签input 标签① 文本框② 密码框③ 单…

Vue中实现路由跳转的三种方式详细分解

vue中实现路由跳转的三种方式 目录 vue中实现路由跳转的三种方式 一、使用vue-router 1.下载vue-router模块到当前工程 2.在main.js中引入VueRouter函数 3.添加到Vue.use()身上 – 注册全局RouterLink和RouterView组件 4.创建路由规则数组 – 路径和组件名对应关系 5…

CSS flex布局最后一行左对齐的常用方法

一、justify-content 在CSS flex布局中&#xff0c;justify-content属性定义了浏览器之间&#xff0c;如何分配顺着弹性容器主轴 (或者网格行轴) 的元素之间及其周围的空间&#xff0c;可以控制列表的水平对齐方式&#xff0c; justify-content: center; /* 居中排列 */ …

Visual Studio 2022 给创建的类/接口加头注释

Visual Studio 2022 给类加注释 找到文件路径 一般在C盘&#xff1a; C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\ItemTemplates\CSharp\Code\2052\Class 如图&#xff1a; 修改这个.cs文件 using System; using System.Collections.Generic…

Linux驱动开发——串口设备驱动

Linux驱动开发——串口设备驱动 一、串口简介 串口全称叫做串行接口&#xff0c;通常也叫做 COM 接口&#xff0c;串行接口指的是数据一个一个的顺序传输&#xff0c;通信线路简单。使用两条线即可实现双向通信&#xff0c;一条用于发送&#xff0c;一条用于接收。串口通信距…

C++ Linux Web Server 面试基础篇-计网(一)

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的在读研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三…

纯代码22步绘制唯美的日夜交替动画(原生HTML+CSS+JS实现,CV即可运行+保姆级步骤教程)

文章目录前言一、HTML(index.html)1. 加入元素二、CSS(style.css)2. 重置浏览器样式3.设置字体样式4.将我们的元素居中5.设置背景颜色6.设置动画显示的位置7.设置夜晚时的背景色8.将动画显示的区域设置为圆型9.绘制白天时天空的样子10.绘制夜晚时天空的样子11.创建昼夜交替动画…

vue3 项目搭建教程(基于create-vue,vite,Vite + Vue)

vue3 项目搭建教程&#xff08;基于create-vue&#xff0c;vite&#xff0c;Vite Vue&#xff09; 目录 一、搭建vue3 项目前提条件 二、通过create-vue搭建vue3 项目 三、搭建一个 Vite 项目 四、构建一个 Vite Vue 项目 五、打开Vue 项目管理器 六、Vite Vue 项目目…

Vue.js笔记------事件

一、事件与事件流 1、事件与事件流 【事件】 JavaScript中的事件&#xff0c;可以理解就是在HTML文档或者浏览器中发生的一种交互操作&#xff0c;使得网页具备互动性。常见的有加载事件、鼠标事件。 【事件流】 由于DOM是一个树结构&#xff0c;如果在父子节点绑定事件时候&am…

前端使用xlsx导出数据生成Excel文件

xlsx的使用安装 xlsx引入 xlsx需要导出的数据源将数据源转成需要的二维数组定义 Excel 表头将定义好的表头添加到 body 中1 创建虚拟的 workbook2 将二维数组转成 sheet!merges 设置单元格合并!cols 设置列宽!rows 设置行高3 向 workbook 中添加 sheet4 导出 workbook 完整示例…

JavaWeb 简单的图书管理系统(jsp+servlet)

关于与需要的文件下载字体颜色分类:黑色 表示正文蓝色 表示一个超链接&#xff0c;点击可以跳转页面橙色 表示一级标题或项目的包名绿色 表示子级内容中含有代码或解释的标题紫色 表示一个代码块的标题红色 表示()外的文件是()内文件的子类,实现类或依赖浅灰 表示解释或其类型高…

Vue封装Axios实现全局的loading自动显示效果

在 vue 项目中&#xff0c;我们通常会使用 Axios 库来与后台进行数据交互。而当我们发起 ajax 请求时&#xff0c;常常需要在页面上显示一个加载框&#xff08;Loading 效果&#xff09;&#xff0c;然后等数据返回后自动将其隐藏。要实现这个功能&#xff0c;我们可以在每次请…

尚品汇前台项目总结

这是我的第一篇个人博客&#xff0c;主要是对我学习前端过程中的一些主记录。由于我在江苏&#xff0c;从2022年过完年&#xff0c;一直到年中&#xff0c;疫情反反复复&#xff0c;又正是大三&#xff0c;马上面临就业难题&#xff0c;我选择了前端。 从html,css,js基础,webAP…