组件实例的三大核心属性——statepropsrefs

Posted MaNqo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了组件实例的三大核心属性——statepropsrefs相关的知识,希望对你有一定的参考价值。

组件实例的三大核心属性

一、state

state的理解

一个组件如果是状态(state)的,那么这个组件就是复杂组件。也就是如果一个组件没有state,这个组件就是简单组件

  • state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)

  • 组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)

    组件的状态里面存着数据,通过改变数据更新组件的状态来更新着页面的展示。

但我们创建类式组件的时候,通过把组件实例对象打印出来,可以看到它的state的值为null的,这个时候我们可以通过对state的操作来完成一些事情。

案例:

下面通过类式组件来实现下面这个案例;

在类中,创建子类要通过调用super来继承父类的东西,而props这个参数也是必不可少的。

在类中的方法默认开启了局部的严格模式,由于changeWeather是作为onClick的回调,所以不是通过实例回调,而是直接调用的,因此要在constructor中先将changeWeather中this的指向强制绑定为组件的实例对象,以便后续对这个实例对象进行操作。

最后在render中设置一个点击事件来调用changeWeather这个函数。

class Weather extends React.Component 
    constructor(props) 
        super(props);
        this.state =  isHot: false ;
        this.demo = this.changeWeather.bind(this);
    
    render() 
        const  isHot = this.state;
        return <h1 onClick=this.demo>今天天气很isHot ? '炎热' : '凉爽'</h1>
    
    changeWeather() 
        const isHot = this.state.isHot;
        console.log(this);
        this.setState( isHot: !isHot );
    

ReactDOM.render(<Weather />, document.getElementById('test'));

在这个过程中,构造器被调用了一次,而render被调用了1+n次,1是初始化组件的那一次,而n就是状态更新的次数。

这里不可以直接通过this.state.isHot = !isHot来修改state的值,因为如果这样子就能够修改的话,把state换做是任何一个单词都可以这样子进行修改,那么state也就没有什么特别的了。

因此,修改状态(state)应该是借助React的一个内置API(setState)去进行修改。

state的简写方式

对上面案例的这份代码进行简化,在开发中一般都用简化后的写法。

直接通过state = isHot: false ;来初始化状态

changeWeather用箭头函数自定义方法(要用赋值语句的形式+箭头函数),使这个this的指向Weather的实例对象。

class Weather extends React.Component 
    state =  isHot: false ;
    render() 
        const  isHot, wind  = this.state;
        return <h1 onClick=this.changeWeather>今天天气很isHot ? '炎热' : '凉爽'</h1>
    
    changeWeather = () =>    // 箭头函数
        const isHot = this.state.isHot;
        this.setState( isHot: !isHot );
    

二、 props

props的理解

props可以接收传递给数组的数据(任何类型都可)

  • 每个组件对象都会有props属性
  • 组件标签的所有属性都保存在props中

引入prop-types用于对组件标签属性进行限制,此时全局多了一个PropTypes对象。

<script type="text/javascript" src="../js/prop-types.js"></script>

在render中传入的属性值后,打印Person实例对象,可以发现传入的值就在props这个属性中。

因此我们就可以通过这个props来实现一些功能。通过propTypes来对标签属性进行类型、必要性的限制,然后通过defaultProps来对这个默认值进行设置,即当没有传入sex时,sex默认为无。

这里的...p运用到了扩展属性,将对象的所有属性通过props传递。

class Person extends React.Component 
    render() 
        console.log(this);
        const  name, age, sex  = this.props;
        return (
            <ul>
                <li>姓名:name</li>
                <li>性别:sex</li>
                <li>年龄:age</li>
            </ul>
        )
    
    static propTypes = 
       name: PropTypes.string.isRequired,
       age: PropTypes.number,
       speak:PropTypes.func  // 限制speak为函数



Person.defaultProps = 
    sex: '无'

const p =  name: "mannqo", age: 18 ;
console.log(...p);
ReactDOM.render(<Person ...p />, document.getElementById('test'));

props(1)只读,即无法直接进行修改,在组件内部不要修改props数据;(2)通过标签属性从组件外向组件向组件内传递变化的数据。简写方式:静态属性,所谓的简写其实就是通过static把限制条件从类的外侧移动到类的里面。

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

  1. 通过给this.state复制对象来初始化内部state
  2. 为事件处理函数绑定实例

在类式组件的构造器中,如果没有传props,直接打印this.props的值为undefined。因此如果需要在构造器中通过this拿到props的话就应该接收props并且传递给super。

constructor(props) 
    super(props);
    console.log(this.props);

函数式组件使用props:(函数式组件也只可以使用props)

function Person(props) 
    const  name, age, sex  = props;
    return (
        <ul>
            <li>姓名:name</li>
            <li>性别:sex</li>
            <li>年龄:age</li>
        </ul>
    )

Person.propTypes = 
    name: PropTypes.string.isRequired,
    sex: PropTypes.string.isRequired,

Person.defaultProps = 
    sex: '女',
    age: 18

ReactDOM.render(<Person name='mannqo' />, document.getElementById('test'));

三、refs

组件类的标签可以定义refs属性来标识,但是ref不能过度使用

1. 字符串形式的ref

string类型的ref存在着一些效率上的问题,一般不推荐使用(官网说它过时了)。

实现下面这段代码:

<input ref="input1" type="text" />

就可以看到实例对象中的refs不再是空,而是refs: input1: input;这样就不需要再给每一个标签设置一个id属性,也省下了document.getElementById这个语句,直接用this.refs.input1就可以获取到这个标签了。

2.回调函数形式的ref

如果ref回调函数是以内联函数的方式定义的,它再更新过程中会被执行两次,第一次传入参数null,第二次传入参数DOM元素。因为每次渲染时会创建一个新的函数实例,React会清空旧的ref并且设置新的,通过ref的回调函数定义成class的绑定函数的方式可以避免上述问题,(但是这个问题可以忽略),实际开发中内联函数的形式写的比较多。

// 内联函数  《最常用》
<input ref=a => this.input1 = a; console.log(a) type="text" />
// 函数 不会重复调用
<input ref=this.saveInput type="text" /> 

jsx注释方式:/* xxx */

3. createRef的使用

React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点。且这个容器只能存一个节点。通过this.myRef.current可以找到ref绑定的那个节点。

myRef = React.createRef();
showInput=(event)=>
    console.log(event);  // 这个event是按钮的绑定事件
    console.log(this.myRef.current.value)

render() 
    return (
        <div>
            <input ref=this.myRef type="text" /> 
            <button onClick="showInput"></button>
        </div>
    )

4. 事件处理

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

    1. React使用的是自定义合成时间,而不是原生DOM事件 —— 兼容性
    2. React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)—— 高效性
  • 通过event.target得到发生事件的DOM元素对象,在调用函数的时候就会自动把这个事件传给这个函数。在发生事件的元素就是这个元素的时候就可以省略ref。但如果是要通过按钮来监听输入框的话(上一个例子)就没办法直接用这种方式实现。

    showDatas = (event) => 
        console.log(event);
    
    render() 
        return (
                <input type="text" onBlur=this.showDatas />
        )
    
    

总结

  1. 组件中render方法中的this为组件实例对象
  2. 组件只定义的方法中的this为undefined时,可以通过函数对象bind()强制绑定this,或者箭头函数
  3. 状态数据不能直接修改或更新,而是用setState去修改
  4. 组件内部不能修改props数据
  5. ref不能过度使用

以上是关于组件实例的三大核心属性——statepropsrefs的主要内容,如果未能解决你的问题,请参考以下文章

React面向组件编程 - 基本理解和使用 - 组件三大核心属性state-props-refs - 事件处理 - 非受控组件 - 受控组件 - 高阶函数 - 函数柯里化

2.基础:Vue组件的核心概念

React系统学习2(组件三大核心属性之state)

React系统学习2(组件三大核心属性之state)

React系统学习2(组件三大核心属性之state)

React组件三大核心属性: state、props、refs