组件实例三大属性

组件实例三大属性

一、三大属性之一:state

1、在构造器中初始化state

默认状态下React.Components会给我们定义构造器(类似于无参构造函数一样),但是默认是把state设置为null的,那么如果我们要自定义初始化的state的话,那么我们就要像(有参构造函数一样)自定义构造函数了

 

如何自定义构造函数呢?

先上代码:

class Weather extends React.Component {
        constructor(props) {
            super(props)
            this.state = {isHot: false}
            this.changeWeacher = this.changeWeacher.bind(this)
        }
        render() {
            return <h1 onClick={this.changeWeacher}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h1>
        }
        // 不写 function
        changeWeacher() {
            console.log(this)
        }
    }
 

其中的构造函数模块:

 constructor(props) {
     super(props)
     this.state = {isHot: false}
     this.changeWeacher = this.changeWeacher.bind(this)
}

一个重要的细节:

自定义构造函数,必须要执行

super(props)

为什么?

并且 为什么super()要放在构造函数最上面执行呢?

ES6语法中,super指代的其实是父类的构造函数,也就是React.Component的构造函数了,

  • 在你调用super( ) 之前,你无法在构造函数中使用this

  • 执行 super(props) 可以使基类 React.Component 初始化 this.props。

  • 另外一个疑问:有时候我们不传 props,只执行 super(),或者没有设置 constructor 的情况下,依然可以在组件内使用 this.props,为什么呢?

    • 其实 React 在组件实例化的时候,马上又给实例设置了一遍 props:

      // React 内部
      const instance = new YourComponent(props);
      instance.props = props;
    • 虽然 React 会在组件实例化的时设置一遍 props,但在 super 调用一直到构造函数结束之前,this.props 依然是未定义的(直接报错了)

      class Button extends React.Component {
        constructor(props) {
          super(); // ? 我们忘了传入 props
          console.log(props);      // ✅ {}
          console.log(this.props); // ? undefined
        }
        // ...
      }

 

 

然后因为state要是对象的模式,所以通过以下语句进行初始化

 this.state = {isHot: false}

 

拓展:类中的方法!

 render() {
   return <h1 onClick={this.changeWeacher()}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h1>
 }

如果是通过 this.changeWeacher() 的话 报错

因为在{ } 内是js语句,所以this.changeWeacher()是直接执行了函数,那么onclick得到的就是这个函数执行的结果了,所以这种方式的话,那么这个函数是一个柯里化函数

  • 柯里化函数:该函数接收一个函数作为参数(比如数组的map方法),或者是该函数返回值是一个函数

如果我们定义函数的时候直接通过:

changeWeacher() {
    console.log(this)
    }

那么这个时候this是undefined的,因为在babal的模式下,也就是严格模式下,changeWeacher函数里面的this是直接执行全局的,但是严格模式下不允许这个发生,所以执行的就是undefined

  • 代码中通过如下方式(通过bind显示硬绑定this,让this执行的是该class类的实例了)

this.changeWeacher = this.changeWeacher.bind(this)

 

在函数中访问和修改state

当该函数的this不管是通过 箭头函数 还是通过显示绑定的方式让this执行了该实例的话,就可以通过this来访问state了

changeWeacher() {
    const isHot = this.state.isHot
    this.setState({isHot: !isHot}) 
}

并且只能通过 this.setState的方式改变state,直接赋值操作改变的话,改变不了状态的(赋值操作可以改变页面数值,但是内部的状态是没有改变的)

 

state的简写方式

class Weather extends React.Component {
            state = {isHot: false}

            render() {
                return <h1 onClick={this.changeWeacher}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h1>
            }

            changeWeacher = () => {
               const isHot = this.state.isHot
               this.setState({isHot: !isHot}) 
            }
        }
  • 不适用构造函数初始化state,直接像定义private属性一样,直接定义类的属性

  • 通过箭头函数的方式定义函数(因为即使在babel严格模式下,箭头函数不会收到严格模式的约束,在箭头函数中使用this访问属性的话,当前作用域找不到的话,就在外面作用域找

    • 所以就可以在箭头函数的this中直接访问实例中定义的state了

 

二、三大属性之二:props

 

props的基本使用

<script type="text/babel">
        class Person extends React.Component{
            render() {
                return (
                    <ul>
                        <li>年级1:{this.props.name}</li>
                        <li>年级2:{this.props.sex}</li>
                        <li>年级3:{this.props.age}</li>
                    </ul>
                )
            }
        }
        // ReactDOM.render(<Person age="19" />, document.getElementById('test'))
        // ReactDOM.render(<Person age={19}/>, document.getElementById('test'))

        const p = {name : 'gogocj', age: '19', sex: '男'}
        ReactDOM.render(<Person {...p} />, document.getElementById('test'))
    </script>
  • 在类中中直接通过this.props访问传递过来的参数

    • 如果是在jsx中的话,就要在 { } 中来访问this.props

  • 传递参数相关

    • 如果想要传输数字的话,要通过 <Person age={19}/>,也就是通过一个{ } 因为在{ }中式js语句,所以19就是js语句中的数字了

    • 通过{ ...p } 三点运算展开符的方式来传递props

 

限制props的类型和默认值

class Person extends React.Component{
            ........
        }
        Person.propTypes = {
            name: PropTypes.string.isRequired,
            speak: PropTypes.func
        }
        Person.defaultProps = {
            sex: '未知'
        }

使用继承了React.Component的类自带的 propTypes属性

  • 要求是串联的:

    PropTypes.string.isRequired  // 表示类型是字符串,并且是必须传的
  • 默认值通过 自带的defaultProps属性

 

简写方式

上面都是直接通过Person.propTypes的方式来对规定类型限制的:

但是Person就是当前的类,所以完全可以省略Person,直接在这个类的内部定义一个static对象就可以了

class Person extends React.Component{
        ............
        static propTypes = {
            name: PropTypes.string.isRequired,
            speak: PropTypes.func
        }
        static defaultProps = {
            sex: '未知'
        }
}

 

在构造器中使用props

constructor(props) {
    super(props)
    // 使用props
}
  • 如果要在构造器中使用的话就必须写 super(props) ,不然在构造器中使用props的话,就会直接的报错了

  • 也就是说,构造器中不适用props就可以不定义,使用的话就要super一下

 

 

三、三大属性之三:refs

前言:

React的诞生很多都是为了减少对document的使用,而我们如果在js中要获取到对应元素的话,传统的方法都是直接使用document的getlementbyid,byClass等等,到那时在React为了减少document操作,使用的是refs

 

字符串类型的refs

  • class Demo extends React.Component{
                showData = () => {
                   const {input01} = this.refs
                   alert(input01.value)
                }
                render() {
                    return (
                        <div>
                            <input id="input1" ref="input01" type="text" placeholder="点击按钮提示数据"/>
                            <button onClick={this.showData}>点我提示左侧的数据</button>
                        </div>
                    )
                }
            }

    我们通过在jsx中,用ref来表示jsx中程序员写的虚拟DOM,但是我们通过this.refs获取到的元素并不是虚拟DOM,而是虚拟DOM转化成真实DOM之后的节点

  • 通过this.refs.input01就可以拿到这个input元素了

 

拓展:React对原生html的相关疯转

在原生里面使用 onclick、onblur等等,但是在React中使用的是onClick、onBlur等等

为什么呢?

React其实是在原生的onclick等等的基础上,进行了相关兼容性的封装,然后改了一下名字,也就是第二个单词首字母大写了,也就是onClick

 

回到函数形式的refs

前言:

字符串形式的ref可能在未来就废除了,因为过多的给原生定义refs来获取该元素信息,方便但是性能太低了

 class Demo extends React.Component{
            showData = () => {
               const {input01} = this
               alert(input01.value)
            }
            render() {
                return (
                    <div>
                        <input  ref={ c => this.input01 = c} type="text" placeholder="点击按钮提示数据"/>
                        <button onClick={this.showData}>点我提示左侧的数据</button>
                    </div>
                )
            }
        }
  • 直接在ref中调用箭头函数,参数就是该元素(代码中用c表示),直接通过this.input01 = c吧这个元素挂载到this上了,因为是箭头函数,所以此时的this执行的是该类实例。

 

使用内联函数的ref

class Demo extends React.Component{
             saveInput = (c) => {
                this.input1 = c
                console.log('@',c)
            }
            render() {
                return (
                    <div>
                       <input ref={ this.saveInput } type="text"/>
                        <button onClick={this.showInfo}>点我</button>   
                    </div>
                )
            }
        }

但是这种内联函数是有一个问题的

  • saveInput中:就是在状态更新的时候会执行两次,第一次c拿到的是null,第二次拿到的才是该元素,更新指的是状态的更新,也就是再一次调用render函数,但是点击和刷新就不会出现这个问题了 ,这是因为在每次渲染render的时候都会创建一个新的函数实例,所以React清空旧的并且设置新的。

 

另一种方式:React.createRef

class Demo extends React.Component{
    
            myRef1 = React.createRef()
            myRef2 = React.createRef()
            
            showData1 = () => {
               alert(this.myRef1.current.value)
            }
            showData2 = () => {
               alert(this.myRef2.current.value)
            }
            
            render() {
                return (
                    <div>
                        <input  ref={this.myRef1} type="text" placeholder="点击按钮提示数据1"/>
                        <button onClick={this.showData1}>点我提示左侧的数据</button>
                        <input onBlur={this.showData2}  ref={this.myRef2} type="text" placeholder="失去焦点提示数据"/>
                    </div>
                )
            }
        }
  • ref={this.myRef} 调用之后,自动把该标签放到这个容器里面

  • 但是该容器是“专人专用”的,如果多个标签同时使用的话,那么后放进去的标签就会把前面的顶掉了

  • 虽然这个是要用多少个就要挂载多少个在实例上,但是这种方式是目前react最推荐的一种方式了

 

 

原文地址:https://www.cnblogs.com/SCAU-gogocj/p/15330552.html