React组件实例的三大属性及事件处理

235 阅读6分钟

React组件实例的三大属性及事件处理

image.png 0. state 0. props 0. refs与事件处理

既然是实例属性,在不用hooks的情况下,只能通过类式组件来使用,所以本章例子都会用类式组件举例

⭐第一个属性:state

state属性中存放着组件的状态,或者说是数据更好理解一些,它里面的值是对象形式(包含多个key-value的组合),我们通过更新组件的state来更新对应的页面显示(重新渲染组件)

在我们创建好的组件实例中,已经有了state属性存在,但其内容为null,那么我们创建组件时,如何在state中定义数据呢?

答:通过构造器添加state状态,或直接对state进行赋值(更推荐)

//创建组件
class MyCpn extends React.Component{
    constructor(props){
        super(props)
        //方式一:通过构造器操作
        this.state = {myState:'hello!'}//在这里添加更改state内容
    }
    //方式二:直接操作
    state = {myState:'hello!'}
    render(){
        return <h1>{this.state.myState}</h1>//在这里进行调用
    }
}

这样,我们就完成了state的初始化以及数据读取调用

注意: state中的属性一旦在构造器中定义,不可直接更改,否则响应式会失效,我们应通过其内置API:setState()进行状态的更改

⭐第二个属性:props

类式组件中使用props

上面的state是创建组件时就定义好了内容,如果我们想要在调用组件时再传入状态,就要用到props啦!

props同样也是组件实例上的属性,其状态的传入方式:

ReactDOM.render(<MyCpn name="何小幸" age="18"/>,document.getElementById('app'));

这样,何小幸和18都传入了组件中,成为prop属性中的状态,我们在组件中通过this.props.name/age就可以取到对应的属性。

如果属性有很多呢?

简化写法:

const info = {name:'何小幸',age:18,sex:"unkonwn",tel:'137...',addr:'山西太原',birth:'010214'}
ReactDOM.render(<MyCpn {...info}/>,document.getElementById('app'))

这样info对象中的数据都会分别传入组件中

限制props中属性的类型与设置默认值

在React版本15.5.0以前,我们使用组件.propTypes来限制属性类型:

//第一种写法:在类外侧定义
MyCpn.propTypes = {
    name:React.PropTypes.string
}
//第二种写法:在类内侧定义
class MyCpn extends React.Component{
    static propTypes = {
        name:React.PropTypes.string
    }
    //其余省略
}

在新版本中,我们需要引入对应的prop-types库:

npm install prop-types --save
​
import PropTypes from 'prop-types';

然后直接通过PropTypes对象来使用:

MyCpn.propTypes = {
    name:PropTypes.string
}

如果传入的value不符合对应类型,控制台就会爆红

这里要注意:string和number都是小写,函数类型写为func

设置属性为必填项:name:PropTypes.string.isRequired

设置默认值:

//第一种写法:在类外侧定义
MyCpn.defaultProps = {
    sex:'不明'
}
​
//第二种写法:在类内侧定义
class MyCpn extends React.Component{
    static defaultProps = {
        name:React.PropTypes.string
    }
    //其余省略
}

props属性是只读的

当你写了构造器时

在你写了构造器时,需要将props作为参数传入,并在构造器中调用super(),同时也要为super传入props参数,否则就会产生props属性丢失问题(通过this.props)无法获取属性

写法:

constructor(props){
    super(props);
    //...
}

如果你并不需要在构造器中通过this.props获取其属性,那么完全可以不用写

函数式组件中使用props

函数式组件中无法绑定属性,我们就要通过参数的形式进行传递:

function MyCpn(props){
    //...
}

在调用组件时,传入的属性就会自动作为参数进行传递,在组件内通过props就可获取。

由于函数内不能使用static关键字,所以如果想要对props中的内容进行限制,只能使用在外侧的方式

MyCpn.propTypes = {
    name:PropTypes.string
}

⭐第三个属性:refs

想知道refs属性,先来看看ref属性,ref类似于id,可以写在标签中作为其唯一标识符:

<input ref='myInput' type='text'/>

这样ref就标识了该input标签

refs属性则是绑定在组件上的属性,为对象类型,它会为我们收集所有标注ref属性的标签,统一以refValue:DomEle的形式存在refs中:

image.png

注:refs为我们收集到的不是虚拟DOM,而是真实DOM

🌰回调refs

由于字符串类型的ref存在效率问题,如今已移除字符串作为其值的用法,取而代之的是用回调函数createRef API的方式代替

回调,顾名思义,传入回调函数作为其属性值,最简单的例子:

<input ref={()=>{console.log('@');}} type='text'/>

这样在打开网页的一瞬间,控制台就会输出@符号

如果我们定义一个参数:

<input ref={a=>console.log(a);} type='text'/>

控制台输出:<input type='text'/>

可以看出,输出的便是回调所在本身的DOM

那么我们就可以这样操作:

<input ref={a=>this.input1 = a;} type='text'/>

这里的this指向和render中的指向相同,为react组件本身,这样就可以在组件上创建一个input1属性,并为其赋值为a,也就是该回调函数所在的DOM

这样我们在自定义函数中要取出input,就可以直接从this中取出。

回调refs的执行次数

如果你的ref回调函数是以内联函数地形式定义的,在render第一次执行时,回调函数会相对应地执行一次,而在更新过程中,会被执行两次,第一次传入参数null,然后第二次传入参数DOM元素,这是因为在每次渲染时会创建一个新的函数实例,所以React清空旧的ref并且设置新的,我们可以通过将ref的回调函数定义为class的绑定函数以避免这个问题

🌰createRef的使用

React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点

也就是说,我们在类中定义:myRef = React.createRef()方法后,再去需要定义ref的标签中:<input ref={this.myRef} type='text'/>,这样该标签就可以存入我们创建的myRef容器中

让我们看看现在容器中的状态:

{current: input}

于是我们通过this.myRef.current就可以取到该input标签实例

但注意:该容器中只能存放一个标签,若想存放多个标签,则要创建多个容器,如果一个容器存放多个标签,那么后存放的就会覆盖掉之前存在的

此方式是目前react最推荐的refs使用形式

⭐事件处理

如果你有心观察,会发现React中的事件与原生js的事件是有些区别的:例如onclickonClick

原生事件都是小写,而React中的事件on后的单词首字母需要大写

这是由于React为了更好的兼容性,使用的是自定义(合成)事件,而不是使用的原生DOM事件,React中的事件是通过事件委托的方式,委托给组件最外层元素进行处理的,最终再通过event.target得到发生事件的DOM元素对象

例如:

<div>
    <button onClick={this.showData}>点我,你点我呀~</button>
</div>

👆这里的onClick就绑定到了外层的容器,div身上

👉当发生事件的元素正好是你要操作的元素,那么就可以直接使用事件绑定,而不是全部用refs

例如:

<input onBlur={this.showData} type="text" placeholder="失去焦点我会提示数据哦~"/>

对应方法:

showData = (event) => {
    alert(event.target.value);//这里显示的是input本身的属性,即发生事件的元素正好是你要操作的元素
}

👆这样便可以防止我们过度使用refs