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

news2024/10/5 15:28:27

总结

一、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元素对象

react

一、React组件

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

组件允许你将 UI 拆分为独立可复用的代码片段,包括JS/CSS/HTML等。

组件从概念上类似于 JavaScript 函数。

它接收参数(即 “props”),内部可以有自己的数据(即 “state”),并返回用于描述页面展示的 React 元素。

一个 React 应用就是由一个个的 React 组件组成的

1.1 函数组件

  • 定义

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

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

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

    • <组件名称 />

<body>
    <!--1、创建容器元素-->
    <div id="root"></div>
    <!--2、引入相关的js文件-->
    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/babel.min.js"></script>
    <!--3、实现js代码-->
    <script type="text/babel">
        //3.1 声明一个函数
        //注意:首字母必须大写!!
        function Header(){
            return <h3>这是一个h3元素</h3>
        }
        //3.2 渲染该函数组件
	    //在 React 内部会调用 Header 函数, 得到虚拟 DOM 对象, 
		//最终将虚拟 DOM 对象转化为真实 DOM, 渲染到容器中
        ReactDOM.render(<Header/>,document.querySelector("#root"));
    </script>
</body>

当如果有多个组件需要同时渲染,一定要注意,结构中必须包含根标签!!!

<script type="text/babel">
        //3.1 声明一个函数
        //注意:首字母必须大写!!
        function Header(){
            return <h3>这是一个h3元素</h3>
        }
        function Nav(){
            return <ul>
                <li>首页</li>    
                <li>列表页</li>
            </ul>
        }
        //3.2 渲染该函数组件
        //渲染单组件
        // ReactDOM.render(<Header/>,document.querySelector("#root"));
        //渲染多组件
        ReactDOM.render(<div>
            <Header></Header>
            <Nav></Nav>    
        </div>,document.querySelector("#root"))
</script>
  1. 组件名首字母必须大写. 因为react以此来区分组件元素/标签 和 一般元素/标签
  2. 组件内部如果有多个标签,必须使用一个根标签包裹.只能有一个根标签
  3. 必须有返回值.返回的内容就是组件呈现的结构, 如果返回值为 null,表示不渲染任何内容
  4. 会在组件标签渲染时调用, 但不会产生实例对象, 没有状态
  5. 后面我们会讲如何在函数组件中定义状态 ==> hook语法

1.2 类组件

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

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

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

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

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

    • <组件名称 />

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!--1、引入react核心文件-->
    <script src="./js/react.development.js"></script>
    <!--2、引入react操作DOM文件-->
    <script src="./js/react-dom.development.js"></script>
    <!--3、babel进行转换jsx语法-->
    <script src="./js/babel.min.js"></script>
    <style>
        header {
            width: 100%;
            height: 100px;
            background: #456;
        }
        .main {
            width: 100%;
            height: 100px;
            background: #569;
        }
    </style>
</head>
<body>
    <div id="root"></div>
    <script type="text/babel">
        class Header extends React.Component {
            //在类中不光有constructor,还有render也是自动调用
            //先调用constructor,主要在类式组件实例对象添加一些初始化的属性
            //在调用render方法,将return后面返回的虚拟DOM元素对象进行返回
            constructor() {
                super();
                console.log(this); //类式组件的实例化对象
                //在实例化对象身上添加初始化属性
                this.a = 1;
                console.log('调用构造器了....')
            }
            render() {
                console.log('调用render方法了...');
                console.log(this); //类式组件的实例化对象
                return <header>头部</header>
            }
        }
        class Main extends React.Component {
            render() {
                return <main className="main">中间</main>
            }
        }
        //创建一个类式组件
        class App extends React.Component {
            render() {
                return <div>
                    <Header></Header>
                    <Main></Main>
                </div>
            }
        }
        //将组件中的虚拟DOM元素对象转化成真实的DOM元素对象,并且挂载到root元素
        //只要一个类式组件调用的时候,都会默认执行实例化,进而得到一个类组件的实例化对象
        ReactDOM.render(<App />, document.querySelector('#root'))
    </script>
</body>
* 使用类式组件的注意事项:
* 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

React 把组件看成是一个状态机(State Machines)。

通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。

React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面。

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

基本使用

  • 初始化 state

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

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

    • 不能直接更新state数据
    • 必须 this.setState({ 要修改的属性数据 }) 异步方法
<body>
    <!--1.准备一个容器 -->
    <div id="app"></div>
    <!--2.引入js文件-->
    <!--2.1 引入react核心库文件-->
    <script src="js/react.development.js"></script>
    <!--2.2 引入react-dom 用于支持react操作DOM-->
    <script src="js/react-dom.development.js"></script>
    <!--2.3 引入babel 用于将jsx转为js-->
    <script src="js/babel.min.js"></script>
    <script type="text/babel">
         //1.创建类式组件
         class Mood extends React.Component{
             constructor() {
                 super();
                 //初始化状态
                 this.state = {
                     isGood:false   //在这个对象中可以根据实际开发需求存入多组key-value
                 }
             }
             render(){
                //使用this.state.isGood这种方式相对较繁琐
                // return <h1>今天心情很{this.state.isGood ? '好' : '不好'}</h1>  
                 
                //也可以使用对象的解构赋值语法.
                const {isGood} = this.state.isGood;
                return <h1>今天心情很{isGood ? '好' : '不好'}</h1>   
             }
         }
         //2.渲染组件到页面
         ReactDOM.render(<Mood/>,document.getElementById("app"))
    </script>
</body>

注意:

  1. React中的state是React实例对象中的其中一个很重要的属性

  2. 当如果想要对一个类的实例对象进行一些初始化的操作

    (增加一个属性 / 修改一个属性的值等)都需要借助class类中的构造器constructor函数来完成。

  3. React官网中要求state的值必须为一个{}对象

  4. class类组件中的render方法中的this表示当前组件实例对象

  • state中的事件绑定
<body>
    <!--1.准备一个容器 -->
    <div id="app"></div>
    <!--2.引入js文件-->
    <!--2.1 引入react核心库文件-->
    <script src="js/react.development.js"></script>
    <!--2.2 引入react-dom 用于支持react操作DOM-->
    <script src="js/react-dom.development.js"></script>
    <!--2.3 引入babel 用于将jsx转为js-->
    <script src="js/babel.min.js"></script>
    <script type="text/babel">
         //1.创建类式组件
         class Mood extends React.Component{
             constructor() {
                 super();
                 this.state = {
                     isGood:false
                 }
             }
             
             //3.声明需要点击的事件函数
             demo(){
                 console.log('标题被点击了~~~');
             }
             render(){
                 //使用this.state.isGood这种方式相对较繁琐
                 // return <h1>今天心情很{this.state.isGood ? '好' : '不好'}</h1>  
                 
                //也可以使用对象的解构赋值语法.
                const {isGood} = this.state.isGood;
                return <h1 onClick={this.demo}>今天心情很{isGood ? '好' : '不好'}</h1>   
             }
         }
         //2.渲染组件到页面
         ReactDOM.render(<Mood/>,document.getElementById("app"))
    </script>
</body>

注意:

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

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

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

  • 类中方法的this

前面咱们已经学到了如何在虚拟DOM元素身上绑定事件,虽然实现了事件绑定,但是之前想要做的事情其实是需

要通过这个点击的事件函数来修改state中的状态属性值,所以就需要来具体研究类中方法的this。

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

原因:

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

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

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

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

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

选择:

  1. 一般用箭头函数方式, 编码简洁
  2. 如果要传递特定的数据, 选择用 包裹箭头函数方式
<body>
    <!--1.准备一个容器 -->
    <div id="app"></div>
    <!--2.引入js文件-->
    <!--2.1 引入react核心库文件-->
    <script src="js/react.development.js"></script>
    <!--2.2 引入react-dom 用于支持react操作DOM-->
    <script src="js/react-dom.development.js"></script>
    <!--2.3 引入babel 用于将jsx转为js-->
    <script src="js/babel.min.js"></script>
    <script type="text/babel">
         //1.创建类式组件
         class Mood extends React.Component{
             constructor() {
                 super();
                 this.state = {
                     isGood:false
                 }
             }
             render(){
                const {isGood} = this.state.isGood;
                return <h1 onClick={this.changeMood}>今天心情很{isGood ? '好' : '不好'}</h1> 
             }
             
             //3.声明需要点击的事件函数(类中定义方法,不能写function)
            /*
             * 1. changeMood具体是声明在哪里的?? --- Mood的原型对象身上,目的让实例对象使用
             * 2. 由于changeMood是作为onClick的回调函数,不是直接通过实例对象来调用,而是直接调用
             * 3. 类中方法默认开启了局部的严格模式,所以changeMood中的this为undefined
            */
            changeMood(){
                console.log('标题被点击了~~~');
            }
         }
         //2.渲染组件到页面
         ReactDOM.render(<Mood/>,document.getElementById("app");
    </script>
</body>

报错:Cannot read property ‘state’ of undefined

原因:默认情况下demo函数中的this指向了window对象,但由于babel在解析代码时开启了严格模式,

​ 禁止自定义的函数里面的this指向window对象,所以为undefined

注意:

  1. 在类中声明的方法都在类的原型对象上。
  2. 在类中声明的方法在函数内部都默认自动开启了严格模式

回忆class中的方法中的this指向问题

//复习class类中方法的this相关问题
class Person{
	constructor(name,age){
		this.name = name;
		this.age = age;
	}
	//在类中声明的方法都在类的原型对象上
	study(){
		console.log('学习');
		console.log(this); 
	}
}
const p1 = new Person('张三',23);
p1.study(); 

在类中声明的方法在函数内部都默认自动开启了严格模式,

<script>
    //复习原生JS中的函数严格模式下的this
	function demo(){
		//在函数内部开启严格模式
    	'use strict';
    	console.log(this); //undefined
	}
	//调用函数
	demo();
</script>

具体如何解决this指向的问题呢?

其实很简单,既然this指向丢失了,那么我们修改成想要的this指向不就行了么?

  • 方式一:箭头函数
<script type="text/babel">
    //1.创建类式组件
    class Mood extends React.Component{
        constructor() {
            super();
            this.state = {
                isGood:false
            }
        }
        render(){
            const {isGood} = this.state;
            return <h1 onClick={this.changeMood}>今天心情很{isGood ? '好' : '不好'}</h1> 
        }

        //3.声明需要点击的事件函数
        changeMood=()=>{
            console.log(this.state.isGood);
        }
    }
    //2.渲染组件到页面
    ReactDOM.render(<Mood/>,document.getElementById("app");
</script>
  • 方式二:bind修改this指向
<script type="text/babel">
    //1.创建类式组件
    class Mood extends React.Component{
        constructor() {
            super();
            this.state = {
                isGood:false
            }
            //在初始化Mood的实例对象的时候,将changeMood中的this进行修改,但是不调用方法
            //由于实例对象身上没有changeMood这个方法,但是会顺着原型链向下查找
            //左侧的this.changeMood是在实例对象身上添加一个叫changeMood的方法
            //右侧的this.changeMood是利用实例对象中向Mood的原型对象身上查找changeMood这个方法
            this.changeMood = this.changeMood.bind(this);
        }
        render(){
            const {isGood} = this.state;
            return <h1 onClick={this.changeMood}>今天心情很{isGood ? '好' : '不好'}</h1> 
        }

        //3.声明需要点击的事件函数
        changeMood(){
            console.log('标题被点击了~~~');
        }
    }
    //2.渲染组件到页面
    ReactDOM.render(<Mood/>,document.getElementById("app");
</script>

  • 方式三:包裹箭头函数
<script type="text/babel">
    //1.创建类式组件
    class Mood extends React.Component{
        constructor() {
            super();
            this.state = {
                isGood:false
            }
        }
        render(){
            const {isGood} = this.state;
            return <h1 onClick={()=>{this.changeMood()}}>
                	今天心情很{isGood ? '好' : '不好'}
				   </h1> 
        }

        //3.声明需要点击的事件函数
        changeMood(){
            console.log(this.state.isGood);
        }
    }
    //2.渲染组件到页面
    ReactDOM.render(<Mood/>,document.getElementById("app");
</script>

  • setState

特别注意:state状态数据不能直接修改或者是更新!!!!,且setState是异步更新

<script type="text/babel">
        //1.创建类式组件
        class Mood extends React.Component {
            constructor() {
                super();
                this.state = { isGood: false }
                this.changeMood = this.changeMood.bind(this);
            }
            render() {
                const { isGood } = this.state;
                return <h1 onClick={this.changeMood}>今天心情很{isGood ? '好' : '不好'}</h1>
            }

             //3.声明需要点击的事件函数
             changeMood() {
                //4. 修改state中的isGood数据
                //const  isGood  = this.state.isGood;
                //this.setState({isGood:!isGood})
                 //或者
                 this.setState(state => {
                    let isGood = !state.isGood
                    console.log(isGood)
                     //需要返回一个对象,对象的键名需要是状态中的属性名
                    return { isGood }
                })
            }
         }
        //2.渲染组件到页面
        ReactDOM.render(<Mood />, document.getElementById("app"))
</script>

虽然前面的代码中咱们已经看到了isGood的状态值可以相互之间进行切换了,

但是还要有一些细节的问题思考一下,

问题1:如果state中的不光有isGood一个属性状态,那么在修改的时候属性是合并还是替换呢??

答:

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

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

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

答:

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

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

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

答:1 + n次

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

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

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

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

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

案例:点击按钮自增更新次数

在这里插入图片描述

<body>
    <!--1.准备一个容器 -->
    <div id="app"></div>
    <!--2.引入js文件-->
    <!--2.1 引入react核心库文件-->
    <script src="js/react.development.js"></script>
    <!--2.2 引入react-dom 用于支持react操作DOM-->
    <script src="js/react-dom.development.js"></script>
    <!--2.3 引入babel 用于将jsx转为js-->
    <script src="js/babel.min.js"></script>
    <script type="text/babel">
         //1.创建类式组件
         class Count extends React.Component{
             constructor() {
                 super();
                 //初始化状态
                 this.state = {
                     count: 0   //在这个对象中可以根据实际开发需求存入多组key-value
                 }
             }
             render(){
                const { count } = this.state;
                return (
                        <div>
                            <span>计数:{count}</span><br />
                            <button onClick={() => {
                                this.setState({
                                    count: count + 1
                                })
                            }}>点击增加次数</button>
                        </div>
        		)
             }
         }
         //2.渲染组件到页面
         ReactDOM.render(<Count/>,document.getElementById("app"))
    </script>
</body>
  • state的简写形式
<body>
    <script>
        class Person{
            constructor(name,age){
                 this.name = name;
                 this.age = age;
                //  this.hobby = '敲代码'
            }
            //在类的所有实例对象身上添加属性
            //虽然是在类中直接添加,但是如果实例中需要从外部传递数据的话,
            //还是需要在构造器中完成,如果是一个固定的值就可以直接在类中定义
            hobby = '敲代码';
        }
        const p1 = new Person('张三',23);
        const p2 = new Person('李四',23);
        console.log(p1);
        console.log(p2);
    </script>
</body>

前面虽然咱们考虑了细节,针对于代码也做了完善,但是仍然考虑不周到,

例如:当事件函数出现了多个时,this全部丢失,难道每一个函数都要在构造函数中修改指向??

<script type="text/babel">
    //1.创建类式组件
    class Mood extends React.Component {
        //初始化状态
        state = { isGood: false , weather:'阴天' }
		render() {
    		const { isGood , weather  } = this.state;
    		return <h1 onClick={this.changeMood}>今天天气是{weather},所以心情很{isGood ? '好' : '不好'}</h1>
		}

		//3.声明需要点击的事件函数
		changeMood = ()=>{
    		//4. 修改state中的isGood数据
    		//箭头函数本身是没有自己的this指向,需要看外侧函数的指向
    		const  isGood  = this.state.isGood;
    		this.setState({isGood:!isGood})
		}
	}
	//2.渲染组件到页面
	ReactDOM.render(<Mood />, document.getElementById("app"))
</script>

总结:

  1. 组件中的render方法中的this为组件实例对象

  2. 组件自定义方法中的this为undefined,如何解决?

    1. 强制绑定this:通过函数对象中的bind
    2. 箭头函数
  3. 状态数据,不能直接修改或更新

  4. 组件自定义方法在类的原型上无法直接定义为一个箭头函数,必须使用赋值成一个箭头函数

2.2 props

  • 介绍

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

  • 作用

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

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

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

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

  • 基本使用
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="js/react.development.js"></script>
    <script src="js/react-dom.development.js"></script>
    <script src="js/babel.min.js"></script>
</head>
<body>
    <div id="root"></div>
    <script type="text/babel">
        //父组件
        class App extends React.Component {
            render() {
                let obj = {
                    name: '张三',
                    age: 23,
                    sex: '男'
                }
                return (
                    <div>
                        <Student name={obj.name} age={obj.age} sex={obj.sex} />
                    </div>
                )
            }
        }
        //子组件
        class Student extends React.Component {
            render() {
                const { name, age, sex } = this.props;
                return (
                    <div>
                        <ul>
                            <li>姓名:{name}</li>
                            <li>年龄:{age}</li>
                            <li>性别:{sex}</li>
                        </ul>
                    </div>
                )
            }
        }
        ReactDOM.render(<App />, document.getElementById('root'));
    </script>
</body>
  • 批量传递props

前面虽然已经GET到了props的好用之处,但是有很多实际的问题隐含在里面。

例如:

a. 当组件实例中存在多个属性,全部罗列在组件上,代码一定会显得非常长,不太好

b. 组件实例中的一些属性数据,大多数不会自己随意定义,而是从服务器中获取出来的

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="js/react.development.js"></script>
    <script src="js/react-dom.development.js"></script>
    <script src="js/babel.min.js"></script>
</head>

<body>
    <div id="root"></div>
    <script type="text/babel">
        //父组件
        class App extends React.Component {
            render() {
                let stus = [
                    {
                        sname: '张三丰',
                        sage: 98,
                        ssex: '男',
                        shobby: ['打太极拳', '耍太极剑'],
                        sheight: 180
                    },
                    {
                        sname: '张无忌',
                        sage: 78,
                        ssex: '男',
                        shobby: ['打太极拳', '耍太极剑'],
                        sheight: 170
                    },
                    {
                        sname: '周芷若',
                        sage: 20,
                        ssex: '女',
                        shobby: ['九阴白骨爪'],
                        sheight: 170
                    }
                ]
                return (
                    <div>
                        {
                            stus.map((item, index) => {
                                return <Student {...item} key={index} />
                            })
                        }

                    </div>
                )
            }
        }

        //子组件
        class Student extends React.Component {
            render() {
                const { sname, sage, ssex, shobby, sheight } = this.props;
                return (
                    <div>
                        <ul>
                            <li>姓名:{sname}</li>
                            <li>年龄:{sage}</li>
                            <li>性别:{ssex}</li>
                            <li>技能:{shobby.join(',')}</li>
                            <li>身高:{sheight}</li>
                        </ul>
                    </div>
                )
            }
        }


        ReactDOM.render(<App />, document.getElementById('root'));
    </script>
</body>

2.2.1 props中的children属性

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

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

2.2.2 props 的特点

  • 可以给组件传递任意类型的数据
  • props 是只读的对象,只能读取属性的值,不要修改props
let { name, age, hobby, fn, isMan } = this.props;
let obj = { a: 1, b: 2 }
this.props = obj;
console.log(this.props); //还是原来的对象值,且还会报错
  • 可以通过...运算符来将对象的多个属性分别传入子组件
  • 如果父组件传入的是动态的 state 数据, 那一旦父组件更新 state 数据, 子组件也会更新
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="js/react.development.js"></script>
    <script src="js/react-dom.development.js"></script>
    <script src="js/babel.min.js"></script>
    <style>
        .btn {
            display: inline-block;
            margin-bottom: 0;
            font-weight: 400;
            text-align: center;
            white-space: nowrap;
            vertical-align: middle;
            -ms-touch-action: manipulation;
            touch-action: manipulation;
            cursor: pointer;
            background-image: none;
            border: 1px solid transparent;
            padding: 6px 12px;
            font-size: 14px;
            line-height: 1.42857143;
            border-radius: 4px;
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
            margin-right: 20px;
        }

        .btn-primary {
            color: #fff;
            background-color: #337ab7;
            border-color: #2e6da4;
        }
    </style>
</head>

<body>
    <div id="root"></div>
    <script type="text/babel">
        //父组件
        class App extends React.Component {
            state = {
                isMan: true
            }
            render() {
                const { isMan } = this.state;
                return <div>
                    <Button
                        name={"张三"}
                        age={23}
                        isMan={isMan}
                        hobby={['吃饭', '睡觉', '打游戏']}
                    />
                    <button className="btn btn-primary" onClick={() => {
                        this.setState({
                            isMan: !this.state.isMan
                        })
                    }}>修改性别的值</button>
                </div>
            }
        }
        //子组件
        class Button extends React.Component {
            render() {
                let { name, age, hobby, fn, isMan } = this.props;
                return (

                    <div>
                        <p>姓名:{name}</p>
                        <p>年龄:{age}</p>
                        {isMan ? <p>性别:男</p> : <p>性别:女</p>}
                        <p>爱好:{hobby.join('/')}</p>

                    </div>
                )
            }
        }


        ReactDOM.render(<App />, document.getElementById('root'));
    </script>
</body>
  • 函数式组件的props
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="js/react.development.js"></script>
    <script src="js/react-dom.development.js"></script>
    <script src="js/babel.min.js"></script>
</head>

<body>
    <div id="root"></div>
    <script type="text/babel">
        //父组件
        class App extends React.Component {
            render() {
                //创建一个对象
                let info = {
                    name: '张三',
                    age: 23,
                    hobby: ['吃饭', '睡觉', '打豆豆']
                }
                return (
                    <div>
                        <Stu {...info} />
                    </div>
                )
            }
        }
        //子组件
        function Stu(props) {
            let { name, age, hobby } = props
            return (
                <div>
                    <p>姓名:{name}</p>
                    <p>年龄:{age}</p>
                    <p>爱好:{hobby.join('-')}</p>
                </div>
            )
        }

        ReactDOM.render(<App />, document.getElementById('root'));
    </script>
</body>

2.3 refs

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

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

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

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

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

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

先来实现一个案例:

需求: 自定义组件, 功能说明如下:

1. 点击按钮, 提示第一个输入框中的值

2. 当第2个输入框失去焦点时, 提示这个输入框中的值

在这里插入图片描述

<script type="text/babel">
         class Demo extends React.Component{
             render(){
                 return (
                     <div>
                        <input ref="ipt1" type="text"/>
                        <button onClick={this.ClickFun}>提示输入数据</button>
                        <input onBlur={this.BlurFun} ref="ipt2" type="text" placeholder="失去焦点提示数据"/>
                     </div>
                 )
             }

             ClickFun = ()=>{
                 const {ipt1} = this.refs;
                 alert(ipt1.value);
             }

             BlurFun = ()=>{
                 const {ipt2} = this.refs;
                 alert(ipt2.value);
             }
         }
         //渲染组件到页面
         ReactDOM.render(<Demo/>,document.getElementById("app"))
</script>
  • 回调函数形式的ref【官方推荐】

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

<script type="text/babel">
         class Demo extends React.Component{
             render(){
                 return (
                     <div>
                        {/* 在组件实例对象身上添加一个属性名(ipt),属性值为当前input节点对象 */}
                        <input ref={(currentNode)=>{this.ipt = currentNode}} type="text"/>
                        <button onClick={this.ClickFun}>提示输入数据</button>
                        <input onBlur={this.BlurFun} ref={(currentNode)=>{this.ipt1 = currentNode}}  type="text" placeholder="失去焦点提示数据"/>
                     </div>
                 )
             }

             ClickFun = ()=>{
                const {ipt} = this;
                alert(ipt.value);
             }

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

思考: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>

案例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qzegCWvF-1687174801129)(../素材\login.png)]

三、React中的事件处理

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

class Demo extends React.Component{
	myRef = React.createRef();
	render(){
		return (
			<div>
                <input ref={this.myRef}  type="text"/>
                <button onClick={this.ClickFun}>提示输入数据</button>
                <input onBlur={this.BlurFun}  type="text"/>
			</div>
		)
     }

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

	BlurFun = (event)=>{
		console.log(event.target.value);
	}
}
//渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById("app"))

总结:

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

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

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

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

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

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

相关文章

一、动画 - 过渡效果transition

内容目录&#xff1a; 过渡动画&#xff1b;过渡动画的属性&#xff1b; 一、过渡动画 过渡&#xff08;transition&#xff09;作用&#xff1a;- 通过过渡可以指定一个属性发生变化时的切换方式- 通过过渡可以创建一些非常好的效果&#xff0c;提升用户的体验现在我们通过一…

nginx的操作手册和nginx的升级

总结 目录 一首先关闭防火墙和下载nginx包 1.安装依赖包 2.创建运行用户与组 3.进入nginx的目录下进行编译安装 4. 编译和编译安装 5.让系统识别nginx的操作命令 ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin/ 6.添加nginx系统服务把nginx服务加入到…

Idea+maven+springboot项目搭建系列--3 整合阿里云Canal完成Mysql数据的监听

前言&#xff1a;在搭建canal 服务之后&#xff0c;项目中就可以连接canal &#xff0c;完成对感兴趣的数据库及其表中数据的变动完成监听&#xff0c;本文依赖于你已经完成了对canal 服务的搭建工作&#xff1b; 1 Cannal 特点&#xff1a; Canal是阿里巴巴开源的一款基于My…

Spring Boot实战:拦截器和监听器的应用指南

当使用Spring Boot时&#xff0c;我们可以通过拦截器&#xff08;Interceptor&#xff09;和监听器&#xff08;Listener&#xff09;来实现对请求和响应的处理。拦截器和监听器提供了一种可插拔的机制&#xff0c;用于在请求处理过程中进行自定义操作&#xff0c;例如记录日志…

使用自动化测试获取手机短信验证码

目前在职测试开发,,写一些脚本,个人认为这职业不科学不应该有的职业,测试就是测试,开发就是开发,运维还是老鸟,这行业总能折腾些莫名其妙的东西出来,刚做这行时学的第一门语言是bash shell, 去新去单位上班直接写了个一键搭建测试环境的测试脚本,本来不想干测试了,好好做微信小…

Linux 学习记录36(C高级篇)

Linux 学习记录36(C高级篇) 本文目录 Linux 学习记录36(C高级篇)一、文件相关指令1. chmod 修改文件用户权限(1. 权限字母表示法(2. 权限8进制表示法 2. 修改文件所属组(1. chgrp(2. chown 能够同时修改多个(3. 创建链接文件>1 ln创建硬链接文件>2 ln -s 创建软链接文件 …

7DGroup性能实施项目日记1

壬寅年 己酉月 丁丑日 2022年9月21日 晴 经过上周的7DGroup学员群内部沟通&#xff0c;我们决定启动一个性能实施项目。 在这个实施项目中&#xff0c;把RESAR性能工程的每个环节都落地一遍&#xff0c;让所有参与培训的学员都可以参与。 在这个项目实施过程中&#xff0c;我打…

苹果照片传输到电脑怎么传?批量传输的技巧!

苹果照片传输到电脑怎么传&#xff1f;照片是苹果手机和电脑之间传输比较频繁的内容。对于刚接触苹果手机的朋友&#xff0c;可能还不是很了解传输方法&#xff0c;鉴于此&#xff0c;我们在这里提供几种有效方法来帮助您完成此项任务。无论您想要使用或不使用 iTunes 将照片从…

CAN Frame详解

CAN Frame是CAN总线通信的基本单位&#xff0c;它有多种类型&#xff0c;其中最常见的是数据帧&#xff08;Data Frame&#xff09;&#xff0c;用于传输数据。数据帧有标准格式&#xff08;Standard Format&#xff09;和扩展格式&#xff08;Extended Format&#xff09;&…

redhat 6.4安装oracle11g RAC (一)

&#xff08;一&#xff09;基础环境 虚拟机环境 &#xff1a;vmware workstation 12 操作系统 &#xff1a; redhat6.4 - 64bit 数据库版本 &#xff1a;11.2.0.4 Last login: Fri Jun 16 18:40:20 2023 from 192.168.186.1 [rootrhel64 ~]# cat /etc/redhat-release Red Ha…

项目描述1

学成在线- 6分片上传&#xff0c;8xxl-job 课程模块开发 分布式事务&#xff0c;消息表 spring-security oauth 用户认证授权 学成在线 学成在线认证授权 一些代码 黑马分布式事物 框架学习 - 若依 / RuoYi-Vue-Plus 统一数据权限 若依数据权限使用 数据权限表结构 1. 设备故…

音视频技术开发周刊 | 298

每周一期&#xff0c;纵览音视频技术领域的干货。 新闻投稿&#xff1a;contributelivevideostack.com。 AI艺术在北京798&#xff0c;展望人工智能与环境的未来 本文很有意思的提出了个假设&#xff0c;通过人工智能和艺术家协作与实践产生环绕地球的叙事&#xff0c;去开启置…

【事故致因】HFACS模型各层级中因素的具体含义及内容归纳

HFACS模型各层级中因素的具体含义及内容归纳 1 HFACS(2000版本)中英文结构图2 定义3 结构组成4 各层级因素及内容4.1 不安全行为4.2 不安全行为的前提条件4.3 不安全监督4.4 组织影响 5 HFACS框架的使用 1 HFACS(2000版本)中英文结构图 英文版本&#xff08;论文首次提出原图&…

接口测试基础知识(使用 Fiddler 抓包、使用 Postman 发起请求、Postman汉化教程)

文章目录 一、 什么是接口二、接口测试的流程三、设计接口测试测试用例常见的点四、使用 Fiddler 进行抓包1. 一个重要设置2. 进行抓包 五、使用 Postman 发起 GET 请求1. 通过API文档得到调用信息2. 通过 Postman 构造 GET 请求 六、使用 Postman 发起 POST 请求1. 得到接口调…

机器学习——朴素贝叶斯(手动代码实现)

朴素的我&#xff0c;决定朴素地徒手实现贝叶斯算法&#xff01; 摒弃sklearn 这个体贴善解人意把一切都打包封装好的妈妈 再见了sklearn 妈妈 我要自己手动实现 哪怕前方困难重重 哪怕我此刻还在发牢骚 但我还是要说&#xff0c;撒哟娜拉sklearn妈 看了知乎阿婆主的分析&#…

在 Maya、ZBrush 和 Substance 3D 中创建女枪手(P2)

今天瑞云渲染小编给大家带来了Ivan Lim 的Female Gunslinger 项目的细目&#xff0c;讨论了他在 Think Tank 的教育过程&#xff0c;并解释了他为什么选择虚幻引擎来呈现这个角色。这篇接着上篇继续拓扑UV、灯光材质、渲染等方面的分享 头发 我开始用一个块来处理头发&#xf…

看看人家那高并发秒杀系统,那叫一个优雅

618&#xff0c;大家剁手了么&#xff1f; 说起618&#xff0c;就不得不提其中较为复杂的秒杀环节了。虽说秒杀只是一个促销活动&#xff0c;但对技术要求不低。 秒杀作为618、双十一等电商活动不可缺少的一环&#xff0c;是一个非常典型的活动场景。秒杀场景的业务特点是限时…

并发知识学习

aqs中有2个队列&#xff0c;一个是同步队列&#xff0c;另外一个是条件队列简单记住&#xff1a;独占没有朋友&#xff0c;所以是null。共享就有朋友&#xff0c;所以是固定的node对象。nextWaiter就是表示节点的模式&#xff0c;在条件队列中指向下一个节点。 一个想要去获取锁…

车载测试范例,如何进行ADAS执行器性能测测试?

概述 执行器性能分为横向性能和纵向性能&#xff0c;横向性能主要指方向盘转向的响应性能&#xff0c;纵向主要包括油门加速性能及刹车减速性能。其中横向性能在ADAS中涉及的功能包括LKA、LDW&#xff0c;跟纵向加/减速性能相关的功能主要是ACC&#xff08;自适应巡航&#xf…

C++数据结构【树状数组】

​ 树状数组 什么是树状数组&#xff1f;树状数组和线段树的区别 树状数组的结构什么是lowbitlowbit如何计算代码实现&#xff1a;补充知识——&&#xff0c;|&#xff0c;^运算&|^注意&#xff1a; 树状数组的基本操作单点修改while循环版代码for循环版代码 单点查询区…