组件实例的三大核心属性——state、props、refs

组件实例的三大核心属性

一、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不能过度使用
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值