• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

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

武飞扬头像
学不死就往死里学
帮助1

总结

一、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>
学新通

案例:

学新通

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

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgagihg
系列文章
更多 icon
同类精品
更多 icon
继续加载