React 面向组件编程 之 类式组件、组件实例的三大核心属性

类式组件

import React, { Component } from "react";

export default class App extends Component {
    render() {
        return <h2>我是类式组件</h2>
    }
}

创建类式组件,必须继承React.Component,render必须写,render必须要有返回值。


组件实例的三大核心属性 1:state

state是组件对象最重要的属性,值必须是对象。

组件被称为状态机,通过更新组件的state来更新对应页面的显式,重新渲染组件。

强烈注意:

  • 组件中的render方法中的this,为组件的实例对象
  • 组件中自定义的方法,this为undefined,如何解决?
    • 第一种方法:通过函数bind强制绑定this
    • 第二种方法:使用赋值语句+箭头函数(主流推荐)
  • state不可以直接更新,必须通过React提供的API来进行更新,setState。
import React, { Component } from "react";

export default class App extends Component {

    // 自定义方法
    test() {
        console.log(this); // undefined
    }

    render() {
        return (
            <button onClick={this.test}>test</button>
        )
    }
}

为什么自定义方法test()的this会为undefined?

答:首先,test方法是放在App的原型对象上的,供实例对象使用。

了解this指向的同学应该知道,在对象上的方法,其this的指向是,谁调用这个方法,就指向谁。

但由于,test是作为onClick的回调,所以并不是通过实例对象去调用的,而是直接调用,因此this的指向本应该是window。

然而类中的方法默认开启了局部的严格模式,所以test中的this为undefined。


解决方法:

第一种,通过函数bind强制绑定this

import React, { Component } from "react";

export default class App extends Component {
    // 如果写了构造器,一定要接受props,和super(props)去传props,不然会出现undefined的bug
    constructor(props) {
        super(props);
        this.test = this.test.bind(this);
    }

    // 自定义方法
    test() {
        console.log(this); // App {props: {…}, context: {…}, refs: {…}, updater: {…}, test: ƒ, …}
    }

    render() {
        return (
            <button onClick={this.test}>test</button>
        )
    }
}

第二种,使用赋值语句+箭头函数(主流推荐)

import React, { Component } from "react";

export default class App extends Component {
    test = () => {
        console.log(this); // App {props: {…}, context: {…}, refs: {…}, updater: {…}, test: ƒ, …}
    }

    render() {
        return (
            <button onClick={this.test}>test</button>
        )
    }
}

为什么将普通函数写成箭头函数就可以使得this指向实例对象呢?

答:箭头函数的this指向,是定义时所在的作用域指向的对象,而不是使用时所在的作用域指向的对象,因此指向实例对象App


类式组件中state的使用:

import React, { Component } from "react";

export default class App extends Component {
    // state初始化状态
    state = {count: 1}

    add = () => {
        let { count } = this.state;
        // 更新state的API
        this.setState({ count: count + 1 });
    }

    render() {
        return (
            <>
                <h2>{ this.state.count }</h2>
                <button onClick={ this.add }>+1</button>
            </>
        )
    }
}

组件实例的三大核心属性 2:props

props是只读的,无法修改。

import React, { Component } from "react";

class App extends Component {
    render() {
        const { name, sex, age } = this.props
        return (
            <ul>
                <li>姓名:{ name }</li>
                <li>性别:{ sex }</li>
                <li>年龄:{ age }</li>
            </ul>
        )
    }
}

// 将App组件标签渲染到index页面的div上
ReactDOM.createRoot(document.getElementById('root')).render(<App name="cxk" sex="女" age="22" />);

批量传递props

let obj = {
    name: 'cxk',
    sex: '女',
    age: '22'
};

ReactDOM.createRoot(document.getElementById('root')).render(<App {...obj} />);

为什么这里的...obj不会报错?

答:众所周知,...展开运算符是无法展开对象的。

但在原生JS中{...obj}的写法,不会报错,因为它是复制对象的一种写法。

然而在React中,{...obj},最外层的{},只是单纯的表示里面要写js表达式了,因此这里真的只是...obj。

但既然对象无法展开,会报错,那为什么这里...obj不会报错呢?

因为React中babel会将...obj翻译,如果在原生JS中...obj必然会报错。


组件实例的三大核心属性 3:refs

refs是React中用来取得某个JSX组件或者某个DOM中的一些状态值的时候,用来获取节点的方法。

在React官方的解释中,它的适用范围如下:

  • 管理焦点,文本选择或媒体播放。
  • 触发强制动画。
  • 集成第三方 DOM 库。

React文档中再三强调,请不要过度使用refs,所以当我们可以用dom原生对象解决时,尽量不要使用refs。


1、字符串形式的ref

这里的ref可以理解为id。

字符串形式的ref已经不被官方推荐使用了,之后很有可能会被删除。

因为string类型的ref存在效率问题,写多了程序会很慢。但是在之前版本的编码中还是有很多人使用,因为它太方便了。

import React, { Component } from "react";

export default class App extends Component {
    showData = () => {
        // 这是获取到一个真实DOM
        console.log(this.refs.input1);
    }
    showData2 = () => {
        alert(this.refs.input2.value)
    }
    render() {
        return (
            <div>
                <input ref="input1" type="text" placeholder="点击按钮提示数据" />&nbsp;
                <button ref="button1" onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                <input ref="input2" type="text" placeholder="失去焦点提示数据" onBlur={this.showData2} />
            </div>
        )
    }
}

2、 回调函数形式的ref

import React, { Component } from "react";

export default class App extends Component {
    showData = () => {
        // 这是获取到一个真实DOM
        console.log(this.input1.value);
    }
    showData2 = () => {
        alert(this.input2.value)
    }
    render() {
        return (
            <div>
                <input ref={c => { this.input1 = c }} type="text" placeholder="点击按钮提示数据" />&nbsp;
                <button ref={c => { this.button1 = c }} onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                <input ref={c => {this.input2 = c }} type="text" placeholder="失去焦点提示数据" onBlur={this.showData2} />
            </div>
        )
    }
}

回调ref中回调执行次数的问题

如果ref回调函数是以内联函数的方式定义的,那么在更新状态state的过程中,它会被执行两次。

第一次传入参数null,然后第二次会传入参数DOM元素,这是因为在每次渲染时会创建一个新的函数实例,所以react为了清空旧的ref并且设置新的。

通过将ref的回调函数定义成class的绑定函数的方式可以避免上述问题,但大多时候这个问题是无关紧要的。

开发中,都是写成内联的。


3、createRef的使用

React官方最推荐的创建ref的方法:

import React, { Component } from "react";

export default class App extends Component {
    myRef = React.createRef();
    myRef2 = React.createRef();

    showData = () => {
        // 这是获取到一个真实DOM
        console.log(this.myRef.current.value);
    }
    showData2 = () => {
        alert(this.myRef2.current.value)
    }
    render() {
        return (
            <div>
                <input ref={ this.myRef } type="text" placeholder="点击按钮提示数据" />&nbsp;
                <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
                <input ref={ this.myRef2 } type="text" placeholder="失去焦点提示数据" onBlur={this.showData2} />
            </div>
        )
    }
}

4、React中的事件处理

请勿过度使用ref。触发事件的元素,和获取数据的元素,是同一个DOM元素的时候,可以不用写ref:

import React, { Component } from "react";

export default class App extends Component {
    showData = (event) => {
        alert(event.target.value);
    }

    render() {
        return (
            <div>
                <input ype="text" placeholder="失去焦点提示数据" onBlur={this.showData} />
            </div>
        )
    }
}
posted @ 2022-09-22 10:13  笔下洛璃  阅读(83)  评论(0编辑  收藏  举报